2017-02-15 1 views
4

まず、テーブル構造と必要な出力を説明しましょう。mysqlのトランザクションごと、またはより良いalgothrimを使用した貸方照合?

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 

私の要件は、FIFO方式でそれぞれ支払わトランザクションまたはデビット取引上の部分有料のベースをマークすることです。このような。これは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で構成されたPart Paid? –

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; 

あなたは、ループを開始し、そのIDをキャプチャし、あなたが最初にクレジットを投稿借方に対してクレジットを適用する場合:このような何かコース)を選択することにより、credit_id =処理中の現在の与信取引額を総額から差し引いたsum(amount)を選択します。

そしてデビットトランザクションの量よりも少ない、またはそれが全くエントリを持たないマップテーブルの和(量)と、最も古い借方を見つけます。

デビット・トランザクションとクレジット・トランザクションを相互参照する行と、デビットに対して適用されたクレジットの額を新しいテーブルに作成することによって、そのトランザクションに適切な額のクレジットをそのトランザクションに適用します。

繰り返しクレジットが使い果たされる、またはすでに完全に資金を供給されていない、それ以上のデビット取引がなくなるまで。このロジックを使用して

は借方間クレジットやクレジットにわたる債務の簡単な分割を可能にし、あなたが探しているものを私は信じてあなたを与えます。

借方が支払われているかどうか

、部分的、または完全なは、そのトランザクションのための新しいテーブルのエントリの合計は、取引の合計未満、0である、またはトランザクションの合計に等しいかどうかによって回答されています。その状態をトランザクション自体に格納せず、必要に応じてこのテーブルを計算し、debit_idでこのテーブルとグループを結合します。

このスキームは、借方と貸方が同じテーブルに格納されるか、2つの異なるテーブルに格納されるかにかかわらず機能します。適切なIDに外部キーを追加します。

ボーナスポイント:

使用ジャンクションテーブルを削除し、更新をブロックするようにトリガします。これらのエントリは、取り消すことができない静的な履歴ファクトを表します。それ以外の場合は履歴を書き換えているため、これらは不変であるとみなすべきです。

ジャンクション・テーブルへの挿入時にトリガを使用して、ジャンクション・テーブルの既存の関連する行と比較すると、現在のデビットまたはクレジットに割り当てられた合計金額が、デビットまたはクレジットこれにより、アプリケーションのバグが潜在的に高価な数学的なエラーになるのを防ぐことができます。

関連する問題