每月计算 运行 总计

Calculate running total per month

假设我有这个 table:

id; date; units
1; Jan 1; 1
2; Jan 2; 4
3; Feb 9; 6
4; Mar 1; 1
5; Mar 4; 2

我现在如何相应地计算 "accumulated_month" 列?计算应在每个新月重新开始。

id; date; units; accumulated_month
1; Jan 1; 1; 1
2; Jan 2; 4; 5
3; Feb 9; 6; 6
4; Mar 1; 1; 1
5; Mar 4; 2; 3

我得到的只有这个:

id; date; units; accumulated_month
1; Jan 1; 1; 5
2; Jan 2; 4; 5
3; Feb 9; 6; 6
4; Mar 1; 1; 3
5; Mar 4; 2; 3
create table t (id int, date date, units int);
insert into t (id, date, units) values
(1, '2016-01-01', 1),
(2, '2016-01-02', 4),
(3, '2016-02-09', 6),
(4, '2016-03-01', 1),
(5, '2016-03-04', 2);

不清楚您是想要月总计还是月内的 运行 总计。本月总计:

select
    id, date, units,
    sum(units) over (partition by date_trunc('month', date)) as acumm
from t
order by 1,2
;
 id |    date    | units | acumm 
----+------------+-------+-------
  1 | 2016-01-01 |     1 |     5
  2 | 2016-01-02 |     4 |     5
  3 | 2016-02-09 |     6 |     6
  4 | 2016-03-01 |     1 |     3
  5 | 2016-03-04 |     2 |     3

如果您想在一个月内获得 运行 总数,请将订单添加到 window 函数:

select
    id, date, units,
    sum(units) over (
        partition by date_trunc('month', date)
        order by id
    ) as acumm
from t
order by 1,2
;
 id |    date    | units | acumm 
----+------------+-------+-------
  1 | 2016-01-01 |     1 |     1
  2 | 2016-01-02 |     4 |     5
  3 | 2016-02-09 |     6 |     6
  4 | 2016-03-01 |     1 |     1
  5 | 2016-03-04 |     2 |     3

运行这个:

SELECT
    t1.id, 
    t1.date, 
    t1.units,
    SUM(t2.units) accumulated_month
FROM t t1 
    JOIN t t2 
        ON date_trunc('month', t1.date) = date_trunc('month', t2.date)
            AND t2.date <= t1.date
GROUP BY t1.id, t1.date, t1.units
ORDER BY t1.id

编辑:

简化SQL:

SELECT
    t1.*, SUM(t2.units) accumulated_month
FROM t t1 
    JOIN t t2 
        ON date_trunc('month', t1.date) = date_trunc('month', t2.date)
            AND t2.date <= t1.date
GROUP BY t1.id
ORDER BY t1.id

逻辑

背后的逻辑是JOIN table 本身。然后对于 t1 JOIN 的每一行,t2 中的所有行都满足 (AND):

  1. 一样year/month
  2. t2.date <= t1.date

有了这些 ON 约束,t1 中的每一行将 JOIN 累积到那个月为止。所以如果不分组,你会得到类似的东西:

╔════╦════════════╦═══════╦════════════╦══════════╗
║ id ║    date    ║ units ║  t2_date   ║ t2_units ║
╠════╬════════════╬═══════╬════════════╬══════════╣
║  1 ║ 2016-01-01 ║     1 ║ 2016-01-01 ║        1 ║
║  2 ║ 2016-01-02 ║     4 ║ 2016-01-01 ║        1 ║
║  2 ║ 2016-01-02 ║     4 ║ 2016-01-02 ║        4 ║
║  3 ║ 2016-02-09 ║     6 ║ 2016-02-09 ║        6 ║
║  4 ║ 2016-03-01 ║     1 ║ 2016-03-01 ║        1 ║
║  5 ║ 2016-03-04 ║     2 ║ 2016-03-01 ║        1 ║
║  5 ║ 2016-03-04 ║     2 ║ 2016-03-04 ║        2 ║
╚════╩════════════╩═══════╩════════════╩══════════╝

GROUP BY t1.id 之后,您可以 SUM(t2.units) 得到您期望的结果。