簡體   English   中英

賬戶余額:如何在 SQL 上正確計算

[英]Account balance: how to calculate it properly on SQL

我正在開發一個應用程序,我必須在其中存儲一些銀行帳戶信息,包括每日帳戶余額。

因此,例如:

17/10/2014 - (+) - Starting/Initial balance - 5,000.00
17/10/2014 - (=) - Balance - 5,000.00
-
18/10/2014 - (-) - Payment - (1,000.00)
18/10/2014 - (=) - Balance - 4,000.00
-
19/10/2014 - (=) - Balance - 4,000.00
-
20/10/2014 - (-) - Payment - (1,000.00)
20/10/2014 - (=) - Balance - 3,000.00

我想我可以創建一個特定的“account_balance”表,我可以在其中存儲每天的每個帳戶余額。

如果我錯了,你能幫我找到最好的方法嗎? 但是,如果我是對的,如何讓數據庫計算每日余額,特別是當用戶開始編輯舊值時如何使數據庫更新余額?

我所說的“舊價值觀”是指:

1 - 這是“帳戶 A”語句的樣子:

18/10/2014 - (+) - Starting/Initial balance - 5,000.00
18/10/2014 - (=) - Balance - 5,000.00
-
19/10/2014 - (=) - Balance - 5,000.00
-
20/10/2014 - (=) - Balance - 5,000.00

2 - 但是用戶忘記登記收入,所以他通過添加新收入來完成(所以現在必須更新余額):

18/10/2014 - (+) - Starting/Initial balance - 5,000.00
18/10/2014 - (+) - Sales commission - 2,500.00 <- USER ADDED THIS.
18/10/2014 - (=) - Balance - 7,500.00 <- THIS BALANCE HAS BEEN UPDATED.
-
19/10/2014 - (=) - Balance - 7,500.00 <- THIS BALANCE HAS BEEN UPDATED.
-
20/10/2014 - (=) - Balance - 7,500.00 <- THIS BALANCE HAS BEEN UPDATED.

而不是存儲余額,有一個表,只為每個用戶存儲交易。

例如:

Date            Transactions        Comment
17/10/2014      +5,000.00           Starting/Initial balance - 
18/10/2014      -1,000.00           Payment
20/10/2014      -1,000.00           Payment

然后你可以創建一個平衡視圖(類似於):

create view balance as
  select userId, sum(transactions) as balance from TransactionTable group by userId

如果您想更精確並包含開始和停止日期(即:能夠在任何時間點獲得平衡),您可以創建一個參數化視圖(尚未嘗試使用日期,但我認為它會起作用同樣)。

此答案適用於 PostgreSQL,OP 在對原始問題的評論中詢問了該答案。

數據完整性對我來說是最重要的。 所以我不願意存儲聚合值,除非 a) 性能很差,並且 b) dbms 可以保證聚合值是正確的。

我從這張桌子開始。

create table transactions (
  trans_id serial primary key,
  cust_id integer not null, -- foreign key, references customers, not shown
  trans_time timestamp not null default current_timestamp,
  trans_amt numeric(14,2) not null 
);

create index on transactions (cust_id);

我選擇了時間戳而不是日期,因為像這樣的應用程序通常需要支持時間戳,而且在一般情況下,它的性能應該比日期差。 如果我們在時間戳方面獲得了良好的性能,那么我們應該能夠在日期方面獲得良好的性能。 我沒有假設單個客戶的時間戳是唯一的。

我將 2000 萬行隨機數據加載到這個表中,然后我更新了統計信息。 數據包括正數和負數。 為了使目視檢查更容易,金額甚至高達數百美元。

此類應用程序中更常見的查詢之一涉及返回單個客戶的登記簿——所有交易都有運行余額。

這是前三天客戶 128 的原始數據。

cust_id  trans_time            trans_amt
--
128      2014-01-01 08:36:09    200.00
128      2014-01-01 14:18:10    200.00
128      2014-01-01 14:26:56      0.00
128      2014-01-01 18:17:31    400.00
128      2014-01-01 20:18:53    100.00
128      2014-01-02 00:10:35      0.00
128      2014-01-02 01:44:26    300.00
128      2014-01-02 15:49:31   -300.00
128      2014-01-03 00:33:23    400.00
128      2014-01-03 11:55:13   -200.00
128      2014-01-03 11:56:34   -100.00
128      2014-01-03 14:58:42   -400.00
128      2014-01-03 17:31:11      0.00

我們應該期待前三天的這些款項。

2014-01-01   900.00
2014-01-02     0.00
2014-01-03  -300.00

前三天的運行余額應該是這樣的。

2014-01-01   900.00
2014-01-02   900.00
2014-01-03   600.00

每日余額登記表

select 
      cust_id
    , trans_date
    , sum(daily_amt) over (partition by cust_id order by trans_date) daily_balance
from (select 
            cust_id
          , trans_time::date trans_date
          , sum(trans_amt) daily_amt
      from transactions
      where cust_id = 128
      group by cust_id, trans_date) x
order by cust_id, trans_date;
cust_id  trans_date   daily_balance
--
128      2014-01-01   900.00
128      2014-01-02   900.00
128      2014-01-03   600.00
. . .

寄存器的執行計划

執行計划顯示上面的查詢運行時間為 12 毫秒。 我認為這對於此類應用程序是合理的,但我可以通過索引表達式 ( trans_time::date ) 或復合索引將運行時間減少到 12 毫秒以下。

"WindowAgg  (cost=7232.14..7252.94 rows=1040 width=40) (actual time=11.728..12.093 rows=294 loops=1)"
"  ->  Sort  (cost=7232.14..7234.74 rows=1040 width=40) (actual time=11.700..11.733 rows=294 loops=1)"
"        Sort Key: transactions.cust_id, ((transactions.trans_time)::date)"
"        Sort Method: quicksort  Memory: 38kB"
"        ->  HashAggregate  (cost=7156.62..7169.62 rows=1040 width=16) (actual time=11.392..11.466 rows=294 loops=1)"
"              ->  Bitmap Heap Scan on transactions  (cost=39.66..7141.89 rows=1964 width=16) (actual time=0.839..9.753 rows=1961 loops=1)"
"                    Recheck Cond: (cust_id = 128)"
"                    ->  Bitmap Index Scan on transactions_cust_id_idx  (cost=0.00..39.17 rows=1964 width=0) (actual time=0.501..0.501 rows=1961 loops=1)"
"                          Index Cond: (cust_id = 128)"
"Total runtime: 12.272 ms"

在我的情況下,下面是表架構

表模式

為此,我提供以下解決方案

SELECT id, user_id, credit, debit,
COALESCE(((SELECT SUM(credit) FROM user_transactions b WHERE b.id <= a.id AND user_id = '7') - (SELECT SUM(debit) FROM user_transactions b WHERE b.id <= a.id AND user_id = '7')), 0) as balance
FROM user_transactions a WHERE user_id = '7' ORDER BY id ASC;

這是結果希望它會幫助你。

在此處輸入圖片說明

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM