在 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 numberSQL 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 中的多列上添加累积总和的主要内容,如果未能解决你的问题,请参考以下文章