在 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_yearcircle_idea_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 包含/排除对等点的工具。见:

Aggregating all values not in the same group

现在,ORDER BY ... ea_month 无法使用月份名称的字符串。 Postgres 会根据语言环境设置按字母顺序排序。

如果您的表中存储了实际的date 值,则可以正确排序。如果没有,我建议将ea_yearea_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 行的累积总和?

django orm 和 postgresql 的累积(运行)总和

在 Postgres 中的多列上添加累积总和

Postgres数据库之聚集函数内核源码学习总结

有没有办法在 PostgreSQL 上计算数值积分?