LINUX.ORG.RU

Вопрос по проектированию БД


0

2

Есть примерно 1000 человек, которые совершают оплату раз в год. Схема формирования цены для каждого такая: «Стоимость по договору» + «Долг за прошлый месяц» + «Пени» - «Скидка» - «Переплата за предыдущий период» = «Итого сколько должен заплатить». Все эти долги, скидки и т.д. присутствуют у единиц из списка. Основная масса платит по договору.

«Стоимость по договору» хранится в отдельной таблице, где хранится остальная информация про договор.

Вопрос: как лучше спроектировать таблицу оплаты? У меня мысль вообще не делать её, а сделать несколько таблиц: пени, скидки и т.д., а потом их через JOIN цеплять для тех у кого есть эти скидки, переплаты и т.д.

★★★★

Как модифицированный вариант можно в таблицах с пнями и скидками хранить запись для каждого человека, если пени или скидки нет, то пусть будет записан ноль. Тогда при расчете опланты можно просто делать сумму по формуле, не задумываясь об условных join'ах.

CARS ★★★★
()

а сделать несколько таблиц: пени, скидки и т.д., а потом их через JOIN цеплять для тех у кого есть эти скидки, переплаты и т.д.

Не порти себе карму. Регистры «выставлено к оплате» и «получена оплата» должны нести в себе все объекты соответствующего класса. То есть Invoice(id, client_id, amount, inv_date, inv_type) - базовые счета. Base_Sum(invoice_id, base_month) - базовые плоатежи за месяц - ссылаются на базовый объект счета, и расшифровывает его (base_month - за какой месяц этот счет). Если ты сделашь отдельные таблицы, то чуть позже проклянешь всё, и будешь переписывать код при добавлении какждого очередного типа платежа или класса выставленной суммы.

no-dashi ★★★★★
()
Ответ на: комментарий от CARS

А чем плох вариант, когда если пени и скидок нет, то вообще не заводить запись. Экономия места будет же, не?

То есть, если у человека есть скидка, то в таблице со скидкой заводится соответствующая запись, а если нет, то ничего не заводится.

keeper_b ★★★★
() автор топика
Ответ на: комментарий от no-dashi

Не совсем понял мысль.

Регистры - это ты имеешь в виду «поля(колонки)»?

И ещё, сведения собираются только для выставления счёта.

Про полученную (фактическую) оплату речи нет.

Про тип платежа не понял, у меня нет ни каких типов платежа, есть просто выставляемая сумма.

Мне кажется хранить в таблице эту сумму будет накладно, в место того, что бы каждый раз считать её заново когда понадобится.

keeper_b ★★★★
() автор топика
Последнее исправление: keeper_b (всего исправлений: 1)
Ответ на: комментарий от keeper_b

Регистр - это список объектов одного назначения. Оно же «класс». Оно же «таблица».

Мне кажется хранить в таблице эту сумму будет накладно, в место того, что бы каждый раз считать её заново когда понадобится.

Тебе именно что «кажется». Если сумму ты считаешь, то рано или поздно случится так, что алгоритм поменяется. Это приведет к тому, что у тебя количество расчетных алгоритмов начнет расти - например, пеня будет начисляться не с третьего дня просрочки, а с третьего рабочего, затем ставка пени станет плавающей - 1% в неделю в течение первого месяца и 5% в месяц во все последующие и т.п.

Это приведет к тому, что в функция «распечатать счет» поломается совместимость - функция в разное время для одних и тех же параметров начнет выдавать разные результаты.

Среди «говноделов-быдлокодеров из говноынтыпрайза» есть одно простое правило - «все данные расчетов сохраняются, при запросе результаты только показываются», и поэтому у них фордфокусвкредит, они толстые и довольные, у них жена-такой-же-планктон и личинкичеловека. А вот у хипстеров такого правила нет, поэтому когда хипстеры начинают реализовывать задачу из реальной жизни, то они лажают. И тогда хипстерам начинают вставлять различные предметы в физиологические отверстия, для того не предназначенные, после чего расстроенные хипстеры идут в рестор и со словами «А что теперь делать осталось?» покупают айфон и идут в крайне популярный среди хипстеров клуб «ЛГБТ и Кук», и они становятся модными-современными-втренде

no-dashi ★★★★★
()
Ответ на: комментарий от keeper_b

То есть, если у человека есть скидка, то в таблице со скидкой заводится соответствующая запись, а если нет, то ничего не заводится.

Все равно под скидки нужна отдельная таблица, поскольку скидка - величина плавающая со временем.

no-dashi ★★★★★
()
Ответ на: комментарий от no-dashi

под скидки нужна отдельная таблица

Так а я это и сказал.

Будет таблица с полями вида: id_договора, год, сумма скидки.

id_договора и год образуют составной ключ.

Про «быдлокодеров» и «хипстеров», если честно не понял.

Можешь изложить яснее?

То есть, кто все эти люди и какое они имеют отношение к моей задаче?

keeper_b ★★★★
() автор топика

Предлагаю взглянуть на задачу немного иначе:

1. Понятие «стоимость по договору» это только сначала кажется свойством «клиента». На самом деле, свойство клиента - это «правило оплаты», у которого есть «стоимость по договору», «дата оплаты», и т.п. свойства.

2. Понятий «долг за прошлый месяц» и «переплата за предыдущий период» не нужно, его можно заменить «балансом лицевого счета клиента».

3. Понятия «пени» не нужно. Заменить его на «правила начисления пени», при желании - хранить их в таблице/таблицах в том или ином виде (например, в качестве параметров алгоритма начисления, но можно и гибче). Все свойства клиента, влияющие на начисление пени должны быть явно выделены в предметной области и должны быть привязаны к клиенту.

4. Понятие «скидка» не нужно. Заменить его на «правила начисления скидки», при желании хранить их в таблице/таблицах, аналогично как с пени. Все свойства клиента, влияющие на начисления скидки должны быть явно выделены в предметной области и должны быть привязаны к клиенту. Просто выданная клиенту постоянная скидка (в процентах, например) оформляется точно так же - как отдельное правило, не зависящее от клиента.

5. Правила начисления пени и правила начисления скидки едины для всех правил оплаты, поэтому можно эти понятия разделить. В более сложном случае пришлось бы придумать как их объединить.

anonymous
()
Ответ на: комментарий от anonymous

Для ясности, как только за расчетный период пени начисляются по правилам начисления пени, они вычитаются из суммы на лицевом счете клиента. Если в следующем месяце правила изменятся, то это будет уже другая история. Подумайте также и о том, чтоб хранить историю операций по каждому каждого счету клиента (кстати, операция по счету это тоже понятие предметной области, выделите его!), историю правил начисления скидки, историю правил начисления пени. Это полезно для разбора конфликтных ситуаций.

anonymous
()
Ответ на: комментарий от keeper_b

Да, именно так. Параметры алгоритма начисления пени (скорее всего будут требования технологов, подтверждающие необходимость хранить историю их изменения, но это тебе виднее).

anonymous
()
Ответ на: комментарий от keeper_b

И таки да, о месте и эффективности не беспокойтесь, по крайней мере сейчас. С таким малым количеством клиентов это не должно сейчас играть роли.

Беспокойтесь о требованиях технологов; о их возможных изменениях; о возможных изменениях алгоритмов начисления скидок и пени и степени этих изменений; о том как вы будете модифицировать слой БД в связи с этими изменениями; о том, как вы будете модифицировать связанные другие слои приложения при этом.

Если вас беспокоит место и эффективность, то сначала столкнитесь с этими проблемами, а когда столкнетесь, спросите технологов и заказчиков, что они об этом думают? Вдруг и проблемы-то нет никакой.

anonymous
()
Ответ на: комментарий от keeper_b

id_договора, год, сумма скидки.

Когда я вижу такие структуры, мне хочется кого-нибудь убить...

У любой сущности, которая действует некоторый срок, должны быть минимум два поля - начало действия и окончание действия.

id_договора и год образуют составной ключ

Это феерический п...ц. А если в середине года «скидка» поменялась? Что делать будешь?

no-dashi ★★★★★
()
Ответ на: комментарий от anonymous

Ой, сейчас подумал, может Вы меня не правильно поняли.

Скидка, пени как раз забиваются вручную оператором.

Там только долг и переплата за предыдущий период считаются.

keeper_b ★★★★
() автор топика
Последнее исправление: keeper_b (всего исправлений: 1)
Ответ на: комментарий от no-dashi

А если в середине года «скидка» поменялась?

Не поменяется. Это юридический не возможно. Более того там на самом деле может быть всего два вида скидки 25% и 50%. Без вариантов.

keeper_b ★★★★
() автор топика
Ответ на: комментарий от keeper_b

Предлагаешь хранить формулу расчёта, например пени, в таблице?

&^*#^%#&*#

Ну вот зачем, зачем ты пытаешься сделать всё наоборот?

НЕ НАДО ХРАНИТЬ ФОРМУЛЫ РАСЧЕТА. Надо хранить его результат. «Формула» это максимум комментарий, незначащий текст. Что ты с этой «формулой» делать будешь? Напишешь свой собственный вычислитель этих формул? Или через eval'оподобные конструкции будешь вычислять?

no-dashi ★★★★★
()
Ответ на: комментарий от keeper_b

Это ты думаешь что невозможно. В следующем году к тебе придет юрист и скажет, что у вас появился четвертый тип договоров, в которых скидка оговаривается приложением к договору и может быть распространена на квартал. И всё, приплыли.

no-dashi ★★★★★
()
Ответ на: комментарий от keeper_b

Ой, сейчас подумал, может Вы меня не правильно поняли.

Он тебя правильнее понял, чем ты сам. А когда к тебе придёт клиент с вопросом: «объясните мне пожалуйста, откуда у меня на счету такие странные цифирки», то и до тебя начнёт доходить мудрость анонимуса.

anonymous
()
Ответ на: комментарий от no-dashi

no-dashi, под «формулой расчета» я понимаю не eval-подобную конструкцию, которую нужно вычислять.

Как выглядит «формула расчета» зависит от нескольких факторов.

1. Если алгоритм расчета постоянен и его параметры постоянны и будут постоянны (что маловероятно), то его можно полностью захардкодить (это всего лишь означает, что мы отложили решение о проектировании этой части и использовали самый прямолинейный метод).

2. Если алгоритм расчета постоянен (что тоже маловероятно), но непостоянны его параметры или их состав, то храним в БД эти параметры и описание их состава.

3. Если алгоритм расчета непостоянен, то мы выделяем его редко меняющуюся часть и его часто меняющуюся часть. Редко меняющуюся часть хардкодим, часто меняющуюся часть структурируем и храним в БД.

Примерно так.

anonymous
()
Ответ на: комментарий от keeper_b

Это означает, что некоторая часть предметной области не представлена в программе явно. Это не значит, что об этой части можно забыть. Она все равно будет влиять, и вылезет впоследствии обязательно, например, в виде неожиданных изменений требований в неожиданных местах, которые вы не сможете так просто реализовать.

Еще это означает, что указанная часть предметной области все равно скорее всего (trust me) содержится в вашей программе, только размыта и в неявном виде, и с ней уже не так просто работать.

Можно принять решение не выражать ее явно в программе, но только если она есть в вашем понимании предметной области и в вашем понимании ее влияния на код и структуры данных, и вы понимаете ущерб и выгоды от своего решения.

anonymous
()
Ответ на: комментарий от anonymous

Спасибо, я и не подумал, что это может быть так ;-)

anonymous
()
Ответ на: комментарий от no-dashi

Или через eval'оподобные конструкции будешь вычислять?

Кстати, раз уж пошла такая пьянка, чем тебе «eval'оподобные конструкции» не угодили? Откуда такие комплексы?

anonymous
()

делай таблицу транзакций и таблицу типов транзакций, вроде «начисление стоимости по договору», «начисление долга», «начисление пени», «скидка»

транзакции
===========
id
дата-время
сумма
тип операции
(дебетовый счёт, кредитный - это уже хардкорная бухгалтерия пошла :)

и в таблице транзакций для клиента пишется последовательность этих операций, по результатам которой будет вычислена сумма денег, сколько клиент должен принести

Harald ★★★★★
()
Ответ на: комментарий от Harald

Спасибо.

А можно какой нибудь пример?

А то с одной таблицей не очень понятно. :)

keeper_b ★★★★
() автор топика
Последнее исправление: keeper_b (всего исправлений: 1)
Ответ на: комментарий от anonymous

чем тебе «eval'оподобные конструкции» не угодили?

Тем, что в последний раз когда я с ними сталкивался, я завел юзера одним запросом на 200+ серверах просто подобрав «правильные параметры» которые попали в этот самый eval.

no-dashi ★★★★★
()

И ещё интересно, а как поступить если человек например сменил фамилию или имя?

keeper_b ★★★★
() автор топика

«Стоимость по договору» хранится в отдельной таблице, где хранится остальная информация про договор.

Подсказываю проверенную схему: номер счёта, баланс. Номер счёта связан с лицом, организацией. Баланс указывает текущую сумму. Дальше из баланса выводит расчёты: в минусе это к оплате, в плюсе - переплата, тупо снятие денег и выписка счет-фактуры (иного док-та). Где хранить, как вычислять пени, мени - не имеет значения. Важно: номер счёта, баланс и выписки/счёта оказанных услуг.

gh0stwizard ★★★★★
()
Последнее исправление: gh0stwizard (всего исправлений: 1)
Ответ на: комментарий от keeper_b

Для этого мы применяли следующий подход.

1. Клиент как сущность предметной области при переименовании или удалении не меняется. Его суррогатный ключ остаётся прежним. Все внешние ключи остаются неизменными. Но для клиента вводится поле признака удаления.

2. Ведётся дополнительная таблица истории клиентов с отдельным суррогатным ключом, и внешним ключом - идентификатором клиента. Каждой записи истории соответствует временной интервал, когда эта запись была актуальна. История пополняется при изменении атрибутов клиентов.

3. В некоторых простых случаях таблицы истории клиентов и текущего состояния клиентов можно объединить, добавляя дату начала и окончания действия записи. Так например ведётся большинство общероссийских классификаторов и справочников. Но при этом нужно чётко понимать когда мы работаем с актуальным состоянием клиентов, а когда с историей.

4. Проводки по счетам ссылаются на таблицу клиентов, но для печати отчётов используется таблица истории клиентов.

5.

anonymous
()
Ответ на: комментарий от kardapoltsev

1000 это только в начале. С каждым годом это число будет увеличиваться на ~500 человек.

keeper_b ★★★★
() автор топика
Ответ на: комментарий от anonymous

А зачем признак удаления в таблице клиента, если мы ведём ещё таблицу истории клиента?

А чего в 5-ом пункте?

keeper_b ★★★★
() автор топика
Ответ на: комментарий от keeper_b

5 пункта нет😊

Признак удаления это особый случай. В большинстве запросов к таблице клиентов вас будут интересовать открытые клиенты. Чтоб не дергать историю по таким мелочам как определение того открыт ли клиент, признак и вводится, вместе с датой закрытия. Если же говорить о высоком, то у закрытии и истории изменений клиента разная природа, и они используются по разному. Так что тут все красиво.

Но вообще, иногда так бывает при проектировании БД - мы умышленно можем допускать избыточность и дублирование, ради упрощения запросов. Теряем в одном, получаем в другом.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.