在PostgreSQL中计算累计和


85

我想查找字段的累积或运行量,并将其从暂存插入表中。我的暂存结构是这样的:

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实现此结果。

谁能建议如何实现这一结果集?


1
您如何在目标表中获得cum_amount 1000?对于CIRCLE_ID,量似乎是2000年

Answers:


130

基本上,您需要一个window函数。如今,这是标准功能。除了正版窗口函数外,您还可以通过添加子句将任何聚合函数用作Postgres中的窗口函数OVER

这里的特殊困难是正确获得分区和排序顺序:

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

每行的总和是从分区的第一行到当前行的计算得出的-或引用该手册以进行精确说明:

默认的取景选项为RANGE UNBOUNDED PRECEDING,与相同RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW。使用 ORDER BY,这会将帧设置为从分区开始到当前行的最后一个ORDER BY对等方的所有行

...这是您要累积或累计的金额。大胆强调我的。

具有相同行的行在此查询中(circle_id, ea_year, ea_month)“对等”。所有这些都显示相同的运行总和,所有对等项都添加到总和中。但我假设您的表UNIQUE位于(circle_id, ea_year, ea_month),则排序顺序是确定的,并且没有行具有对等项。

现在,ORDER BY ... ea_month 将无法使用月份名称的字符串。Postgres将根据区域设置按字母顺序排序。

如果date表中存储有实际值,则可以正确排序。如果没有,我建议更换ea_year,并ea_month与一列mondate在表格中。

  • 改变您所拥有的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;

感谢您的解决方案。您能再帮我一件事情吗?我想使用游标实现同一件事,逻辑是每个圆圈一年中只有一个记录。该功能应该每月运行一次。我该如何实现?
Yousuf Sultan

4
@YousufSultan:大多数情况下,有比光标更好的解决方案。那绝对是一个新问题的东西。请开始一个新问题。
Erwin Brandstetter 2014年

我发现这个答案是不完整的,至少没有注意到这里发生了“框架”,默认为range unbounded preceding,与相同range between unbounded preceding and current row。这就是为什么sum()在用作窗口函数时会产生运行总计的原因-而其他窗口函数没有此默认框架。
Colin't Hart

1
@ Colin'tHart:我在上面添加了更多内容以进行澄清。
Erwin Brandstetter

这是带有类似查询的链接,它具有一个更简单的查询(PARTITION创建运行总计并不总是需
要这样做
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.