Как организовать правильную логику заработка/выплат?
Всем привет!
Есть задача написать систему, в которой у компании будет отражаться движение денег. Грубо говоря, мы оформляем заказ для компании, берем оплату, затем, за вычетом процентов, выплачиваем эти деньги компании.
Есть вопрос, как это реализовать технически в базе данных
Вариант 1, две таблицы, заработок и выплаты. Все хорошо, до тех пор, пока эти таблицы не становятся очень большими. В таком случае подсчет необходимой суммы для выплаты может занять недопустимо большое время (сложить все в таблице заработка и вычесть все из таблицы выплат).
Вариант 2, таблица заработка с отметкой "выплачено или нет" (boolean). В таком варианте все проще, все считается быстро. Но есть одна проблема. Иногда компании нужна внеочередная выплата. И компания может указать сумму выплаты, при которой не получится отметить определенное число заказов, как "выплаченные". Допустим, у компании 3 заказа, каждый по 600 долларов, а компания заказывает выплату тысячи долларов. Ограничивать сумму, чтобы получалось конечное число заказов, не хочется. Можно сделать костыль в виде таблицы внеочередных выплат, но мне кажется, что должно быть другое решение. Оно есть? :)
Roman Kitaev: В прошлый раз задержки начались где-то с 20 миллионов в таблице заработка и 1 миллионе (тут могу ошибаться) в таблице выплат. Проблемы были при формировании ведомости на выплату, так как она формируется по всем клиентам (было около 1000 клиентов)
А так по мне:
1) таблица приходов денег за заказ (фактически это историчность)
2) таблица текущий счет (при поступление или списание денежных средств мы тупо делаем UPDATE одной строки)
P.S. возможно, я что-то не так понял.
Антон Иванов: 20 миллионов - не очень большая таблица. Индексы какие стояли? Как таблица выглядела? Какие запросы? Сколько оперативы на сервере? Какая БД? Как тюнили?
Roman Kitaev: Структуру и индексы я сейчас не вспомню, это был 2008 год :) Отвечая на комментарий выше, шардинг был, помогал, но недостаточно. Оперативки было 8 гигов, база была - postgres последний на конец 2008 года. Исходя из вопросов я понимаю, что надо идти по первому варианту, так? Просто больше внимания уделить индексам и производительности сервера, верно?
Антон Иванов: Если у вас есть постоянно используемая таблица платежей, но нужно хранить данные из нее за долгие годы, возможно, имеет смысл добавить архивную таблицу с аналогичной структурой. И выбрасывать в нее записи возрастом, скажем, более полугода. Она может работать медленно, для архива это не так важно. А текучка перестанет тормозить.
Логика выборки, естественно, усложнится. Если вы не сможете четко отделить запросы текучки от запросов к архиву - лучше и не начинать.
Adamos: Примерно так и было сделано в 2008м. Была отдельная таблица с ранними записями. Если клиент заказывал выборку с период, который захватывал и архивные даты, то была выборка с union'ом, если нет, то только из текущей таблицы
Антон Иванов: Есть еще один странный вариант: разделить таблицу "вертикально" - на две параллельные. В первой будет id и только те поля, по которым может идти выборка - скажем, id клиента и дата платежа. Во второй - тот же id и вся прочая информация.
По первой таблице, за счет ее небольшого размера, будет быстро проходить выборка. По второй выборка будет простейшей - по id - и тоже получится быстрой. Если, конечно, не намудрить с запросами. Останется только объединить результаты.
Я бы сделал журнал где отражал все движения (все +-). Что-б можно было восстановить/пересчитать все производные таблицы.
Параллельно раскладывать суммы по счетам (по строкам, одна компания/человек - одна строка), у кого сколько денег после движения.
Очередная выплата - заносим в журнал, вычитаем из счета.
Итого: работает быстро + есть информация для восстановления если что не так.
Архитектор информационных систем и баз данных. Ful
Задача решается через OLTP с OLAP. Как Вы будете хранить исходные данные от пользователя для начислений и выплат и обрабатывать транзакции это уже другой вопрос. А для учета взаиморасчетов с сотрудниками делаете таблицу транзакций по взаиморасчетам например: (Дата, Сотрудник, Начисление/Выплата, Сумма) и таблицу агрегата суммы расчетов например: (Месяц,Сотрудник,Сумма начислений, Сумма выплат,Сальдо конечное). Дальше соответственно при записи данных от пользователя(OLTP) создаете транзакцию и обновляете для нее агрегаты по месяцам с сальдо(OLAP).