【问题标题】:Add cumulative total sum over many columns in Postgres在 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] 标签。

标签: sql postgresql aggregate-functions postgresql-9.1 window-functions


【解决方案1】:

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

你可以:

  1. 将行转换为一个集合(“counter-pivot”)。
  2. 运行窗口聚合函数sum() OVER (...)
  3. 将集合转换回一行(“枢轴”)。
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 上的相关回答:

也可以使用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;

考虑:

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 个...感谢您的建议...
    猜你喜欢
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    • 2018-07-02
    • 1970-01-01
    • 2019-10-07
    • 1970-01-01
    • 2023-03-14
    相关资源
    最近更新 更多