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

Posted

技术标签:

【中文标题】在 Postgres 中的多列上添加累积总和【英文标题】:Add cumulative total sum over many columns in Postgres 【发布时间】:2015-09-10 08:28:50 【问题描述】:

我的桌子是这样的:

+----+--------+--------+--------+---------+
| id | type   | c1     | c2     | c3      |
+----+--------+--------+--------+---------+
| a  |      0 | 10     |     10 | 10      |
| a  |      0 | 0      |     10 |         |
| a  |      0 | 50     |     10 |         |
| c  |      0 |        |     10 | 20      |
| c  |      0 |        |     10 |         |
+----+--------+--------+--------+---------+

我需要这样的输出:

+----+---------+--------+--------+---------+
| id | type    | c1     | c2     | c3      |
+----+---------+--------+--------+---------+
| a  |       0 | 10     |     10 | 10      |
| a  |       0 | 0      |     10 |         |
| a  |       0 | 50     |     10 |         |
| c  |       0 |        |     10 | 20      |
| c  |       0 |        |     10 |         |
+----+---------+--------+--------+---------+
|total |     0 | 60     |     50 |  30     |
+------------------------------------------+
|cumulative| 0 | 60     |   110  | 140     |
+------------------------------------------+

到目前为止我的查询:

WITH res_1 AS 
  (SELECT id,c1,c3,c3 FROM cloud10k.dash_reportcard),
  res_2 AS 
  (SELECT 'TOTAL'::VARCHAR, SUM(c1),SUM(c2),SUM(c3) FROM cloud10k.dash_reportcard)       
SELECT * FROM res_1
UNION ALL 
SELECT * FROM res_2;

每列产生一个总和 total。 如何添加累计总额

注意:演示有3个数据列,我的实际表有250多个。

【问题讨论】:

您在 Postgres 中寻找解决方案,所以我删除了 [mysql] 标签。 【参考方案1】:

为列的总和一遍又一遍地列出 250 列会非常乏味且效率越来越低 - 这是一个伪装的 O(n²) 问题。实际上,您需要等效的窗口函数来计算 columns 而不是 rows 的运行总数。

你可以:

    将行转换为一个集合(“counter-pivot”)。 运行窗口聚合函数sum() OVER (...)。 将集合转换回一行(“枢轴”)。
WITH total AS (
  SELECT 'total'::text AS id, 0 AS type
       , sum(c1) AS s1, sum(c2) AS s2, sum(c3) AS s3  -- more ...
  FROM   cloud10k.dash_reportcard
  )
TABLE cloud10k.dash_reportcard

UNION ALL  
TABLE total

UNION ALL
SELECT 'cumulative', 0, a[1], a[2], a[3]  -- more ...
FROM  (
   SELECT ARRAY(
      SELECT sum(v.s) OVER (ORDER BY rn)
      FROM   total
           , LATERAL (VALUES (1, s1), (2, s2), (3, s3)) v(rn, s)  -- more ...
      )::int[] AS a
   ) sub;

LATERAL requires Postgres 9.3+.

dba.SE 上的相关回答:

SELECT DISTINCT on multiple columns

也可以使用crosstab() from the tablefunc module 完成步骤,但为了这个简单,只需聚合成一个数组并将每个元素放入一个单独的列中就更简单了。

Postgres 9.1 的替代方案

同上,但是:

...
UNION ALL
SELECT 'cumulative'::text, 0, a[1], a[2], a[3]  -- more ...
FROM  (
   SELECT ARRAY(
      SELECT sum(v.s) OVER (ORDER BY rn)
      FROM  (
         SELECT row_number() OVER (), s
         FROM   unnest((SELECT ARRAY[s1, s2, s3] FROM total)) s  -- more ...
         ) v(rn, s)
      )::int[] AS a
   ) sub;

考虑:

PostgreSQL unnest() with element number

SQL Fiddle 演示两者。

【讨论】:

【参考方案2】:

只需添加另一个CTE 即可获得累积行数:

WITH res_1 AS 
  (SELECT id,c1,c2,c3 
   FROM dash_reportcard),
  res_2 AS 
  (SELECT 'TOTAL'::VARCHAR, SUM(c1) AS sumC1,
          SUM(c2) AS sumC2, SUM(c3) AS sumC3 
   FROM dash_reportcard),
   res_3 AS
   (SELECT 'CUMULATIVE'::VARCHAR, sumC1,
           sumC2+sumC1, sumC1+sumC2+sumC3 
    FROM res_2)
SELECT * FROM res_1
UNION ALL 
SELECT * FROM res_2
UNION ALL 
SELECT * FROM res_3;

Demo here

【讨论】:

您好 Giorgos,非常感谢您.... 他们还有其他方式吗? Y 因为超过 250 列 .... @Madhulandoll 如果你有一个超过 250 列的表,那么你应该认真考虑重新设计你的模式。有这么多列的表几乎肯定会违反 1NF。【参考方案3】:
WITH total AS (
  SELECT 'TOTAL'::VARCHAR, SUM(c1) AS sumc1, SUM(c2) AS sumc2, SUM(c3) AS sumc3
  FROM cloud10k.dash_reportcard
), cum_total AS (
  SELECT 'CUMULATIVE'::varchar, sumc1, sumc1+sumc2, sumc1+sumc2+sumc3
  FROM total
)
SELECT id, c1, c2, c3 FROM cloud10k.dash_reportcard
UNION ALL 
SELECT * FROM total
UNION ALL
SELECT * FROM cum_total;

【讨论】:

嗨帕特里克,非常感谢你......他们还有其他方式吗? Y 因为列或超过 250 个...感谢您的建议...

以上是关于在 Postgres 中的多列上添加累积总和的主要内容,如果未能解决你的问题,请参考以下文章

Postgres 中的累积百分比添加

如何创建按列分组的累积总和

如何在python中的字典列表中查找项目的累积总和

如果在某些时间/值之间,熊猫累积总和

SQL 计算基于 Hive 列中先前值重置的累积总和

OpenMP 中的并行累积(前缀)总和:线程之间的通信值