在 PostgreSQL 中计算累积和
Posted
技术标签:
【中文标题】在 PostgreSQL 中计算累积和【英文标题】:Calculating Cumulative Sum in PostgreSQL 【发布时间】:2014-04-03 14:47:28 【问题描述】:我想找到字段的累积或运行量并将其从暂存到表中。我的暂存结构是这样的:
ea_month id amount ea_year circle_id
April 92570 1000 2014 1
April 92571 3000 2014 2
April 92572 2000 2014 3
March 92573 3000 2014 1
March 92574 2500 2014 2
March 92575 3750 2014 3
February 92576 2000 2014 1
February 92577 2500 2014 2
February 92578 1450 2014 3
我希望我的目标表看起来像这样:
ea_month id amount ea_year circle_id cum_amt
February 92576 1000 2014 1 1000
March 92573 3000 2014 1 4000
April 92570 2000 2014 1 6000
February 92577 3000 2014 2 3000
March 92574 2500 2014 2 5500
April 92571 3750 2014 2 9250
February 92578 2000 2014 3 2000
March 92575 2500 2014 3 4500
April 92572 1450 2014 3 5950
我真的很困惑如何实现这个结果。我想使用 PostgreSQL 来实现这个结果。
谁能建议如何实现这个结果集?
【问题讨论】:
如何获得目标表中的 cum_amount 1000?对于 circle_id,金额似乎是 2000。 @user1724295 他按ea_year
、circle_id
、ea_month
分组。然后想拍cum_amt
【参考方案1】:
基本上,您需要window function。这是当今的标准功能。除了真正的窗口函数外,您还可以通过附加OVER
子句将 any 聚合函数用作 Postgres 中的窗口函数。
这里的特殊困难是正确的分区和排序:
SELECT ea_month, id, amount, ea_year, circle_id
, sum(amount) OVER (PARTITION BY circle_id
ORDER BY ea_year, ea_month) AS cum_amt
FROM tbl
ORDER BY circle_id, month;
没有GROUP BY
.
每一行的总和是从分区中的第一行到当前行计算的——或者更准确地说是引用the manual:
默认的成帧选项是
RANGE UNBOUNDED PRECEDING
,即 与RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
相同。和ORDER BY
,这会将框架设置为 分区中的所有行 从当前行的最后一个ORDER BY
对等点开始。
...这是您所追求的累积或运行总和。大胆强调我的。
具有相同(circle_id, ea_year, ea_month)
的行在此查询中是“peers”。所有这些都显示相同的运行总和,所有对等方都添加到总和中。但我假设您的表是(circle_id, ea_year, ea_month)
上的UNIQUE
,那么排序顺序是确定性的,并且没有行有同行。
Postgres 11 添加了使用新的frame_exclusion
options 包含/排除对等点的工具。见:
现在,ORDER BY ... ea_month
无法使用月份名称的字符串。 Postgres 会根据语言环境设置按字母顺序排序。
如果您的表中存储了实际的date
值,则可以正确排序。如果没有,我建议将ea_year
和ea_month
替换为表中date
类型的单列mon
。
用to_date()
改变你所拥有的:
to_date(ea_year || ea_month , 'YYYYMonth') AS mon
为了显示,可以用to_char()
获取原始字符串:
to_char(mon, 'Month') AS ea_month
to_char(mon, 'YYYY') AS ea_year
虽然坚持不幸的设计,但这会起作用:
SELECT ea_month, id, amount, ea_year, circle_id
, sum(amount) OVER (PARTITION BY circle_id ORDER BY mon) AS cum_amt
FROM (SELECT *, to_date(ea_year || ea_month, 'YYYYMonth') AS mon FROM tbl)
ORDER BY circle_id, mon;
【讨论】:
感谢您的解决方案。您能帮我做一件事吗?我想使用游标来实现同样的事情,逻辑是每个圆圈在一年的一个月中只有一条记录。该功能应该每月运行一次。我怎样才能做到这一点? @YousufSultan:大多数时候有比游标更好的解决方案。这绝对是一个新问题的东西。请开始一个新问题。 我发现这个答案不完整,至少没有注意这里有“框架”,默认为range unbounded preceding
,与range between unbounded preceding and current row
相同。这就是为什么sum()
当用作窗口函数时会产生一个运行总计——而其他窗口函数没有这个默认框架。
@Colin'tHart:我在上面添加了一些内容来澄清。
这里是一个类似问题的链接,查询更简单(PARTITION
并不总是需要创建运行总计):***.com/a/5700744/175830以上是关于在 PostgreSQL 中计算累积和的主要内容,如果未能解决你的问题,请参考以下文章
postgres array_agg 错误:无法累积不同维度的数组
如何计算 Postgresql 中特定日期最后 7 行的累积总和?