【问题标题】:Calculating running sum starting x years before从 x 年前开始计算运行总和
【发布时间】:2012-10-25 13:02:05
【问题描述】:

我有一个实体名称、年份和活动编号的表 波纹管。有些年份没有任何活动。

name | year | act_num
-----+------+---------
aa   | 2000 |       2
aa   | 2001 |       6
aa   | 2002 |       9
aa   | 2003 |      15
aa   | 2005 |      17
b    | 2000 |       3
b    | 2002 |       4
b    | 2003 |       9
b    | 2005 |      12
b    | 2006 |       2

在 postgresql 上创建它;

CREATE TABLE entity_year_activity (
name character varying(10),
year integer,
act_num integer
);

INSERT INTO entity_year_activity
VALUES
    ('aa', 2000, 2),
    ('aa', 2001, 6),
    ('aa', 2002, 9),
    ('aa', 2003, 15),
    ('aa', 2005, 17),
    ('b', 2000, 3),
    ('b', 2002, 4),
    ('b', 2003, 9),
    ('b', 2005, 12),
    ('b', 2006, 2);

我想知道过去 x 年的总数 每年每个实体的今年活动数量如下。

以 x = 三年为例。

name | year | act_num | total_3_years
-----+------+---------+---------------
aa   | 2000 |       2 |      2
aa   | 2001 |       6 |      8
aa   | 2002 |       9 |     17
aa   | 2003 |      15 |     30
aa   | 2004 |       0 |     24
aa   | 2005 |      17 |     32
b    | 2000 |       3 |      3
b    | 2001 |       0 |      3
b    | 2002 |       4 |      7
b    | 2003 |       9 |     13
b    | 2005 |      12 |     21
b    | 2006 |       2 |     14

【问题讨论】:

  • 好问题。样本数据、预期输出、DDL。值得注意的是,Stack Overflow 不保留选项卡,因此您的 COPY 输出被破坏了。最好使用COPY ... CSV

标签: sql postgresql select sum


【解决方案1】:

这是一种使用sum 聚合作为具有基于范围的窗口框架的窗口函数的方法 - 请参阅SUM(...) OVER (PARTITION BY name ORDER BY year ROWS 2 PRECEDING)window framing

WITH name_years(gen_name, gen_year) AS (
  SELECT gen_name, s
  FROM generate_series(
    (SELECT min(year) FROM entity_year_activity),
    (SELECT max(year) FROM entity_year_activity)
  ) s CROSS JOIN (SELECT DISTINCT name FROM entity_year_activity) n(gen_name)
),
windowed_history(name, year,act_num,last3_actnum) AS (
  SELECT
    gen_name, gen_year, coalesce( act_num, 0),
    SUM(coalesce(act_num,0)) OVER (PARTITION BY gen_name ORDER BY gen_year ROWS 2 PRECEDING)
  FROM name_years 
  LEFT OUTER JOIN entity_year_activity ON (gen_name = name AND gen_year = year)
)
SELECT name, year, act_num, sum(last3_actnum) as total_3_years
FROM windowed_history
GROUP BY name, year, act_num
HAVING sum(last3_actnum) <> 0
ORDER BY name, year;

SQLFiddle

需要为本身没有条目的年份生成条目使此查询复杂化。我生成了一个包含所有 (name, year) 对的表,然后在进行窗口求和之前在其上生成 left outer join entity_year_activity,因此所有名称集的所有年份都被表示出来。这就是为什么事情如此复杂。然后我过滤聚合结果以排除总和为零的条目。

【讨论】:

  • 这也没有跳过缺失的年份,所以不正确。
  • @GordonLinoff ...因此“还”。
【解决方案2】:

SQL Fiddle

select
    s.name,
    d "year",
    coalesce(act_num, 0) act_num,
    coalesce(act_num, 0)
    + lag(coalesce(act_num, 0), 1, 0) over(partition by s.name order by d)
    + lag(coalesce(act_num, 0), 2, 0) over(partition by s.name order by d)
    total_3_years
from
    entity_year_activity eya
    right join (
        generate_series(
            (select min("year") from entity_year_activity),
            (select max("year") from entity_year_activity)
        ) d cross join (
        select distinct name
        from entity_year_activity
        ) f
    ) s on s.name = eya.name and s.d = eya."year"
order by s.name, d

【讨论】:

  • 这不起作用,因为它不会跳过没有数据的年份。
  • @GordonLinoff 新版本可以。
【解决方案3】:
SELECT en_key.name, en_key.year, en_key.act_num, SUM(en_sum.act_num) as total_3_years
FROM entity_year_activity en_key
  INNER JOIN entity_year_activity en_sum
     ON en_key.name = en_sum.name
WHERE en_sum.year BETWEEN en_key.year - 2 AND en_key.year
GROUP BY en_key.name, en_key.year

【讨论】:

  • 如果您通过将where 子句移动到on 子句并删除en_key.year = en_sum.year 来解决此问题,那么它将起作用。
  • 已编辑删除 en_key.year = en_sum.year。这确实违背了目的。我不确定是否需要将 where 子句移至 on 子句。我认为无论哪种方式都可以。从风格上讲,我不认为它是连接的一部分,而是报告中的一个条件。我希望这是一个意见和偏好问题。
  • 。 .作为一般惯例,连接条件应该放在on 子句中,所以从理论上讲,它属于那里而不是where 子句。不过两个版本的性能应该是一样的。
  • 是的,但我说这不是一个连接条件,它是一个常规的 where 条件。或者更确切地说,如果它是一个连接条件或不是一个意见。
【解决方案4】:

再试一次。不过,这个缺少 0 行年份:

select t1.name, t1.year, t1.act_num,
      (select sum(t2.act_num) from entity_year_activity t2
                              where t2.year between t1.year - 2 and t1.year
                                    and t2.name = t1.name) total
from entity_year_activity t1;

【讨论】:

    猜你喜欢
    • 2019-08-11
    • 2013-04-17
    • 2010-11-09
    • 1970-01-01
    • 1970-01-01
    • 2018-01-12
    • 1970-01-01
    • 1970-01-01
    • 2016-11-23
    相关资源
    最近更新 更多