2017-02-15 2 views
4

Сначала позвольте мне объяснить структуру таблицы и мой требуемый результат.Соответствующий дебет с кредитом за транзакцию в mysql или с лучшим альготримом?

userid date   amount 
123 2017-01-01   5 
123 2017-01-03   2 
124 2017-01-04   2 
124 2017-01-04   3 
123 2017-01-05   -2 

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

select date as BookingDate, 
     if(amount<0,amount,0) as Debit_Amount, 
     if(amount>0,amount,0) as Credit_Amount, 
     (@runtot := amount + @runtot) AS Balance 
from transactions, 
    (SELECT @runtot:=0) c 
where userid=123 


BookingDate Debit_Amount Credit_Amount Balance 
2017-01-01 0     5   5 
2017-01-03 0     2   7 
2017-01-05 -2    0   5 

Моего требования, чтобы отметить каждую сделку платной или частичную платную основу по дебетовым операциям с методом ФИФО. Как это. Возможно ли это с помощью запроса mysql или с лучшим алгоритмом?

userid date   amount status 
    123 2017-01-01   5 partial_paid(-2) 
    123 2017-01-03   2 
    124 2017-01-04   2 
    124 2017-01-04   3 
    123 2017-01-05   -2 

Благодаря

ответ

1
MariaDB [sandbox]> create table t(userid int,dt date, amount int); 
Query OK, 0 rows affected (0.28 sec) 

MariaDB [sandbox]> truncate table t; 
Query OK, 0 rows affected (0.20 sec) 

MariaDB [sandbox]> insert into t values 
    -> (123 , '2017-01-01' ,  5) , 
    -> (123 , '2017-01-03' ,  2), 
    -> (124 , '2017-01-04' ,  2) , 
    -> (124 , '2017-01-04' ,  3), 
    -> (123 , '2017-01-05' ,  -2), 
    -> (125 , '2017-01-01' ,  5) , 
    -> (125 , '2017-01-03' ,  2), 
    -> (125 , '2017-01-05' ,  -6), 
    -> (126 , '2017-01-01' ,  5) , 
    -> (126 , '2017-01-02' ,  -10), 
    -> (126 , '2017-01-03' ,  2), 
    -> (126 , '2017-01-05' ,  -10), 
    -> (126 , '2017-01-06' ,  13); 
Query OK, 13 rows affected (0.06 sec) 
Records: 13 Duplicates: 0 Warnings: 0 

MariaDB [sandbox]> 
MariaDB [sandbox]> 
MariaDB [sandbox]> 
MariaDB [sandbox]> select s.userid,s.dt,s.amount, 
    -> case when s.crs is null then 0 else s.crs end crs, 
    -> case when s.exhaust is null then 0 else s.exhaust end exhaust, 
    -> case when s.amount > 0 and s.amount <= s.crs and s.crs > 0 then 'Fully paid' 
    -> when s.amount > 0 and s.amount > s.crs and s.crs > 0 then concat('Part paid -' ,s.crs) 
    -> else '' 
    -> end msg 
    -> from 
    -> (
    -> select t1.*, 
    -> if(t1.userid <> @p , 
    -> @crs:=(select sum(t2.amount) * - 1 from t t2 where t2.userid = t1.userid and t2.amount < 0) 
    -> ,@crs:[email protected]) crs, 
    -> if(t1.amount < 0 ,@crs:[email protected],if (t1.amount > @crs , @crs:=0,@crs:[email protected] - t1.amount)) exhaust, 
    -> @p:=t1.userid p 
    -> 
    -> from (select @p:=0,@crs:=0) p ,t t1 
    -> order by t1.userid, t1.dt 
    ->) s 
    -> ; 
+--------+------------+--------+------+---------+--------------+ 
| userid | dt   | amount | crs | exhaust | msg   | 
+--------+------------+--------+------+---------+--------------+ 
| 123 | 2017-01-01 |  5 | 2 | 0  | Part paid -2 | 
| 123 | 2017-01-03 |  2 | 0 | 0  |    | 
| 123 | 2017-01-05 |  -2 | 0 | 0  |    | 
| 124 | 2017-01-04 |  2 | 0 | 0  |    | 
| 124 | 2017-01-04 |  3 | 0 | 0  |    | 
| 125 | 2017-01-01 |  5 | 6 | 1  | Fully paid | 
| 125 | 2017-01-03 |  2 | 1 | 0  | Part paid -1 | 
| 125 | 2017-01-05 |  -6 | 0 | 0  |    | 
| 126 | 2017-01-01 |  5 | 20 | 15  | Fully paid | 
| 126 | 2017-01-02 | -10 | 15 | 15  |    | 
| 126 | 2017-01-03 |  2 | 15 | 13  | Fully paid | 
| 126 | 2017-01-05 | -10 | 13 | 13  |    | 
| 126 | 2017-01-06 |  13 | 13 | 0  | Fully paid | 
+--------+------------+--------+------+---------+--------------+ 
13 rows in set (0.03 sec) 

Осторожно: - Я не в полной мере испытал это !!

+0

@Salmon благодарит за ответ, но я не думаю, что он будет работать для миллионов записей? –

+0

Можно ли добавить дату выхлопа? –

+0

Как бы вы показали полную оплату, которая состояла из n Part Paids? Или часть Платная, которая состояла из n Part Paids? –

1

Для связывания дебетовых транзакций и кредитных операций вам нужна таблица соединений. Что-то вроде этого:

CREATE TABLE debit_credit_map (
    debit_id INT NOT NULL, 
    credit_id INT NOT NULL, 
    amount DECIMAL(14,2) NOT NULL, 
    applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 
    PRIMARY KEY(debit_id, credit_id), 
    KEY(credit_id) 
)ENGINE=InnoDB; 

Если вы хотите использовать кредит против дебет вы первый пост кредит, захватить его идентификатор, а затем начать цикл - проверить, сколько из кредита Непримененное (изначально равна нулю, из курс), выбрав сумму (сумму), где credit_id = текущая кредитная транзакция, которую вы обрабатываете, вычитается из общей суммы кредита.

Затем найдите самый старый дебет с суммой в таблице карт, которая меньше суммы дебетовой транзакции или вообще не имеет записей.

Применять столько кредитов к этой транзакции, как это подходит для этой транзакции, создавая строку в новой таблице, перекрестно ссылающуюся на дебетовую транзакцию с кредитной транзакцией и сумму кредита, применяемую к дебету.

Повторяйте до тех пор, пока кредит не будет исчерпан или нет более дебетовых транзакций, которые еще не полностью финансируются.

Использование этой логики позволяет легко распределять долги по кредитам или кредитам через дебет и дает вам то, что, я считаю, вы ищете.

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

Эта схема работает, если дебетовые и кредитные ресурсы хранятся в одной таблице или двух разных таблицах. При необходимости добавьте внешние ключи в идентификаторы.

Бонусные баллы:

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

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

Смежные вопросы