数据库有一个transactions
表,其中包含以下列:account_id
,date
,transaction_value
(有符号整数)。另一个表(account_value
)存储每个帐户的当前总价值,即每个帐户的所有transaction_value
的总和。它使用transactions
表上的触发器进行更新(即INSERT,UPDATE和DELETE到transactions
触发触发器以更改account_value
。)
新要求是仅在过去365天内计算帐户的总交易价值 。只需要当前的运行总计,而不是之前的总计。此值将经常被请求,几乎与account_value
一样频繁。
你如何有效地实现这个“滑动窗口总和”?一张新表是好的。有没有办法避免每次都超过一年的范围?
答案 0 :(得分:1)
这么简单吗?
SELECT
SUM(transaction_value), account_id
FROM
transactions t
WHERE
-- SQL Server, Sybase t.DATE >= DATEADD(year, -1, GETDATE())
-- MySQL t.DATE >= DATE_SUB(NOW(), INTERVAL 12 MONTH)
GROUP BY
account_id;
您可能希望使用DATE(MySQL)或SQL Server中的this way从日期表达式中删除时间组件
答案 1 :(得分:1)
如果事务表的查询比插入事务表更频繁,那么可能还有一个视图可以用?
答案 2 :(得分:1)
这可以通过标准窗口函数来完成:
SELECT account_id,
sum(transaction_value) over (partition by account_id order by date)
FROM transactions
order by
条款中的over()
使得总和为“滑动总和”。
对于“仅过去356天”,您需要第二个查询来限制WHERE子句中的行。
以上工作在PostgreSQL,Oracle,DB2和(我认为)Teradata。 SQL Server不支持窗口定义中的顺序(即将推出的Denali版本将为AFAIK)
答案 3 :(得分:0)
您将需要一个一次性脚本来填充现有表格,其中包含每个现有记录的前一年的值 - 对于生成的每条记录,需要在前一年的整个过程中运行。
一旦填充了滚动年份列,对上一年求和的一种替代方法是将每个新记录的值派生为前一记录的滚动年值,加上自上次更新以来的交易值减去交易值在最后一次更新之前的一年和从现在开始的一年之前。
我建议尝试两种方法来对抗真实的测试数据以确定哪些方法会表现更好 - 我希望将全年的总和至少在数据相对稀疏的情况下执行,而差异方法可能会更好,如果数据是经常在每个帐户上更新。
答案 4 :(得分:0)
我会在这里避免使用任何实际的SQL,因为它会因您使用的各种SQL而变化很大。
你说你有一个触发器来维持现有的运行总量。
我认为它也(或者每晚进程)在account_value
表中创建新的每日记录。那么INSERT,UPDATE和DELETE会触发触发器来增加或减少现有的运行总量吗?
您需要做的唯一改变是:
- 添加一个新字段,“annual_value”或其他东西
- 以与现有字段相同的方式更新现有触发器
- 使用gbn的答案类型来创建今天的记录(或者你回溯到目前为止)
- 但是以略微不同的方式初始化每个新的每日记录......
当您为新的一天插入新行时,应将其初始化为yesterday's value
- the value 365 days ago
。之后,行为应该与您已经习惯的行为相同。