此答案适用于 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(成本=7232.14..7252.94 行=1040 宽度=40)(实际时间=11.728..12.093 行=294 循环=1)”
“ -> 排序(成本=7232.14..7234.74 行=1040 宽度=40)(实际时间=11.700..11.733 行=294 循环=1)”
" 排序键:transactions.cust_id, ((transactions.trans_time)::date)"
“排序方法:快速排序内存:38kB”
“ -> HashAggregate(成本=7156.62..7169.62 行=1040 宽度=16)(实际时间=11.392..11.466 行=294 循环=1)”
“ -> 事务上的位图堆扫描(成本=39.66..7141.89 行=1964 宽度=16)(实际时间=0.839..9.753 行=1961 循环=1)”
“重新检查条件:(cust_id = 128)”
“ -> transactions_cust_id_idx 上的位图索引扫描(成本=0.00..39.17 行=1964 宽度=0)(实际时间=0.501..0.501 行=1961 循环=1)”
“索引条件:(cust_id = 128)”
“总运行时间:12.272 毫秒”