news 2026/10/10 3:13:34

用SQL执行累计值汇总的几种方法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用SQL执行累计值汇总的几种方法

有这么一个需求,要对某个表的某列,按累计分组计数汇总输出。
比如:表t列a的数据如下:

┌───┐ │ a │ ├───┤ │ 1 │ │ 2 │ │ 3 │ │ 1 │ │ 4 │ └───┘

现在要把a>=1、a>=2、a>=3、a>=4的个数分别汇总输出,得到如下的结果:

┌───┬──────┐ │ a │ cnt2 │ ├───┼──────┤ │ 1 │ 5 │ │ 2 │ 3 │ │ 3 │ 2 │ │ 4 │ 1 │ └───┴──────┘

下面以duckdb数据库为例,把代码稍作修改,也能在postgresql上实现。
方法1:用case when分组和分析函数sum累计
先建立表t,

create table t as (select * from values (1), (2), (3), (1), (4) t(a));

在postgresql中需要把values子句包含在一对小括号中,即:

create table t as (select * from (values (1), (2), (3), (1), (4)) t(a));

然后输入以下查询:

select case when a>=1 then 1 when a>=2 then 2 when a>=3 then 3 when a>=4 then 4 end x, count(1) cnt from t group by x;

得到:

┌───┬─────┐ │ x │ cnt │ ├───┼─────┤ │ 1 │ 5 │ └───┴─────┘

这不是我们需要的结果,因为case when 有短路的性质,满足前面某一个条件(case)的统计以后,就不再判断以后的条件。
这样,因为所有行都满足a>=1,所以,全部行都被统计到了x=1的分组,而其他分组没有再被统计。
容易想到,把条件从>=4到>=1倒序排列,可以求出满足每个分组的计数。

select case when a>=4 then 4 when a>=3 then 3 when a>=2 then 2 when a>=1 then 1 end x, count(1) cnt from t group by x; ┌───┬─────┐ │ x │ cnt │ ├───┼─────┤ │ 1 │ 2 │ │ 2 │ 1 │ │ 3 │ 1 │ │ 4 │ 1 │ └───┴─────┘

这仍然不是我们要求的结果,因为case when 仍然存在短路问题,这种写法实际上隐含地滤掉了同时满足前一个条件的结果,比如这里的when a>=3,实际上是when a>=3 and a<4,不包含a>=4的结果,因为a>=4的行在前一个条件when a>=4已经被统计,就不会再次被统计。需要把这个结果再次累计汇总,才能得到要求的结果。

with t2 as( select case when a>=4 then 4 when a>=3 then 3 when a>=2 then 2 when a>=1 then 1 end x, count(1) cnt from t group by x) select x,cnt,sum(cnt)over(order by x desc)cnt2 from t2 order by x; ┌───┬─────┬──────┐ │ x │ cnt │ cnt2 │ ├───┼─────┼──────┤ │ 1 │ 2 │ 5 │ │ 2 │ 1 │ 3 │ │ 3 │ 1 │ 2 │ │ 4 │ 1 │ 1 │ └───┴─────┴──────┘

为了明显起见,这个查询保留了原查询的cnt和通过sum(cnt)over(order by x desc)新汇总的cnt2两列,这个分析函数写法的含义是,按照x从大到小的顺序,即逆序( desc)对cnt列累计求和,这样,cnt2列a>=4累计的结果就是原查询a>=4的cnt值,保持现状,a>=3累计的结果就是上一步a>=4累计值和a>=3的cnt值之和,a>=2累计的结果就是上一步a>=3累计值和a>=2的cnt值之和,以此类推。
所以,x和cnt2列就是所需的结果。

方法2:利用多个case when打分组标记,然后通过标记的存在性统计
先看打完标记的情况:

select case when a>=1 then 1 else 'a' end || case when a>=2 then 2 else 'a' end || case when a>=3 then 3 else 'a' end || case when a>=4 then 4 else 'a' end x, count(1) cnt from t group by x; ┌──────┬─────┐ │ x │ cnt │ ├──────┼─────┤ │ 1aaa │ 2 │ │ 12aa │ 1 │ │ 123a │ 1 │ │ 1234 │ 1 │ └──────┴─────┘

在这个结果中,x列现在包含一个字符串,其中用字符n标出了满足第n个条件,cnt列的的统计结果与前一种方法第一步的中间结果一致,然后针对这个结果用sum(case when)方法,凡是出现字符1的都被统计到1组,出现字符2的都被统计到2组,以此类推。
为了防止出现短路现象,用了一个包含1到4的临时表做笛卡尔积,把临时表中出现每个字符,都和上述x字符串进行比较,这样包含字符串1的第14行都被统计到1组,包含字符串2的第24行都被统计到2组,以此类推,实现了累计的效果。
在postgresql中,对列的类型一致性要求更严格,需要把代码1、2、3、4用单引号括起来,表示字符类型。

查询语句和结果如下:

with t2 as ( select case when a>=1 then 1 else 'a' end || case when a>=2 then 2 else 'a' end || case when a>=3 then 3 else 'a' end || case when a>=4 then 4 else 'a' end x, count(1) cnt from t group by x) select a, sum(case when instr(x,a::varchar)>0 then cnt end)cnt2 from t2,values(1),(2),(3),(4) t3(a) group by a order by a; ┌───┬──────┐ │ a │ cnt2 │ ├───┼──────┤ │ 1 │ 5 │ │ 2 │ 3 │ │ 3 │ 2 │ │ 4 │ 1 │ └───┴──────┘

在postgresql中,要用strpos代替instr。

方法3:利用码表笛卡尔积对代码分组
在实现上述方法2的过程中,考虑到第一步把x值转变成代码,第二步从代码判断,能否合成一步呢?从而有如下的思路,先建立一张码表code,列出每个代码表示的上下限,然后就省去了打标记的步骤,直接根据x的值统计即可。

with code(c,low,high) as( values (1,1,9999), (2,2,9999), (3,3,9999), (4,4,9999) ) select c, count(case when a>=low and a<high then 1 end)cnt2 from t,code group by c order by c; ┌───┬──────┐ │ c │ cnt2 │ ├───┼──────┤ │ 1 │ 5 │ │ 2 │ 3 │ │ 3 │ 2 │ │ 4 │ 1 │ └───┴──────┘

上述语句中,上限9999是一个超出范围的大数,使得a<high永远为真,可根据实际需求调整,比如,下列查询得到不累计的计数:

with code(c,low,high) as( values (1,1,2), (2,2,3), (3,3,4), (4,4,9999) ) select c, count(case when a>=low and a<high then 1 end)cnt2 from t,code group by c order by c; ┌───┬──────┐ │ c │ cnt2 │ ├───┼──────┤ │ 1 │ 2 │ │ 2 │ 1 │ │ 3 │ 1 │ │ 4 │ 1 │ └───┴──────┘

方法4:用union all合并多个条件汇总结果

select 1 x, count(1) cnt from t where a>=1 union all select 2 x, count(1) cnt from t where a>=2 union all select 3 x, count(1) cnt from t where a>=3 union all select 4 x, count(1) cnt from t where a>=4 ; ┌───┬─────┐ │ x │ cnt │ ├───┼─────┤ │ 1 │ 5 │ │ 2 │ 3 │ │ 3 │ 2 │ │ 4 │ 1 │ └───┴─────┘

完全用手工统计每种条件的结果,然后把结果合并,是最容易的方法。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 4:25:32

Thinkphp和Laravel框架的校园点歌系统的设计与实现

目录摘要技术选型对比开发技术源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;摘要 校园点歌系统是一种基于Web的应用程序&#xff0c;旨在为学生和教职工提供便捷的点歌服务&#xff0c;丰富校园文化生活。系统采用ThinkPHP或Laravel框…

作者头像 李华
网站建设 2026/10/8 15:52:00

沃伦·巴菲特的公司文化评估方法

沃伦巴菲特的公司文化评估方法 关键词:沃伦巴菲特、公司文化评估、投资决策、企业价值观、文化指标 摘要:本文深入探讨沃伦巴菲特的公司文化评估方法。从背景介绍入手,阐述其目的、预期读者等内容。详细剖析公司文化评估的核心概念与联系,给出原理和架构示意图。介绍核心算…

作者头像 李华
网站建设 2026/10/8 17:59:06

当导弹在天上玩漂移:手把手调教气动力控制

基于气动力的导弹姿态控制&#xff08;含MATLAB仿真&#xff09;&#xff0c;提供基于气动力控制的导弹姿态控制律设计参考文献&#xff0c;同时提供MATLAB仿真源代码&#xff0c;源代码内包含定义导弹、大气、地球、初始位置、速度、弹道、姿态、舵偏角、控制律、飞行力学方程…

作者头像 李华
网站建设 2026/10/5 4:54:07

三相逆变器并网控制这玩意儿,玩的就是个电流环套娃。今儿咱们拆个电网电流外环+电容电流内环的骚操作,直接上硬货

三相并网逆变器双闭环控制&#xff0c;电网电流外环电容电流内环控制算法&#xff0c;matlab/Simulink仿真模型&#xff0c;有源阻尼&#xff0c;单位功率因数&#xff0c;电网电压和电流同相位。 先整个控制结构图镇楼&#xff08;此处脑补Simulink模型截图&#xff09;。核心…

作者头像 李华
网站建设 2026/10/6 4:13:24

Chrome现已集成Gemini,仅需4步即可开启。

大家好&#xff0c;我是岳哥。最近Google又将自家的Gemini集成到Chrome上去了&#xff0c;堪称史诗级更新&#xff0c;虽然之前也有不少厂商将AI集成到浏览器&#xff0c;例如微软的copilot。可以一遍刷着网页一边跟Gemini对话&#xff0c;就像这样。这是知乎上的一个帖子图片&…

作者头像 李华