mysql 如何在SQL中正确计算账户余额

yzuktlbb  于 2023-02-11  发布在  Mysql
关注(0)|答案(3)|浏览(335)

我正在开发一个应用程序,我将不得不存储一些银行帐户信息,包括每日帐户余额。
例如:

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.
pw136qt2

pw136qt21#

不要存储余额,而是使用一个表来存储每个用户的交易。
例如:

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

如果您希望更精确,并包括开始和停止日期(即:能够在任何时间点获得平衡),您可以创建一个parametrized view(还没有尝试使用日期,但我认为它也会工作)。

zysjyyx4

zysjyyx42#

这个答案是针对PostgreSQL的,OP在对原始问题的评论中提出了这个问题。
数据完整性对我来说是最重要的,所以我不愿意存储聚合值,除非a)性能很差,b)dbms可以保证聚合值是正确的。
我从这张table开始。

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);

我选择了timestamp而不是date,因为像这样的应用程序通常需要支持timestamp,而且在一般情况下,它的性能比date差。如果我们使用timestamp获得了良好的性能,那么使用date也应该能够获得良好的性能。我没有假设单个客户的timestamp是唯一的。
我在这个表中加载了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"
rbpvctlc

rbpvctlc3#

在我的案例中,下面是表模式

为此,我提供以下解决方案

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;

这是结果,希望对你有帮助。

相关问题