DB2聚合函数SUM、AVG、COUNT有哪些高级用法?

来源:前端技术作者:越南程序员头衔:程序员
导读:本期聚焦于越南程序员创作的《DB2聚合函数SUM、AVG、COUNT有哪些高级用法?》,敬请观看详情。统计报表里经常需要同时计算总额、平均值和记录条数,但DB2的SUM、AVG、COUNT远不止基础计算这么简单。空值处理、去重统计、窗口累计、分组小计等场景都有专门的写法。本文从实际查询入手,讲解DISTINCT与聚合的组合、分区窗口中的累计求和和移动平均,以及CASE WHEN配合聚合实现条件统计,并分析执行计划差异和性能注意点。通过ROLLUP、CUBE和GROUPING SETS可以一次性获得多层级汇总结果,避免多次扫描表。掌握这些高级用法后,能够在不增加应用代码复杂度的前提下,让统计查询更高效、更易维护。

DB2的SUM、AVG、COUNT是日常统计查询中最常用的三个聚合函数,但很多高级场景下它们的行为与基础用法存在明显差异。如果忽视NULL值处理、DISTINCT去重规则、窗口分区以及分组扩展,统计结果可能会与业务预期产生偏差,甚至导致性能问题。本文围绕这三个函数展开,结合真实SQL示例,说明如何在DB2中实现条件汇总、累计求和、移动平均以及多层次小计。

DB2聚合函数SUM、AVG、COUNT有哪些高级用法?

一、NULL值与DISTINCT:聚合结果为什么总是不对

聚合函数最容易产生误解的地方在于NULL值处理。DB2中,COUNT(*)统计所有行,包括整行全为NULL的记录;COUNT(列名)只统计该列非NULL的行数。SUM和AVG会直接忽略NULL值,但它们返回的结果可能因为忽略NULL而造成平均值基数变小。例如一个销售明细表里,quantity列存在NULL,SUM(quantity)得到的是有效数量的总和,而COUNT(*)减去COUNT(quantity)就是缺失数量记录的条数。如果不区分这两类计数,计算平均数量时用SUM(quantity)/COUNT(*)就会得到偏小的结果。推荐始终明确使用AVG(quantity)而不是手动计算,避免NULL处理遗漏。

DISTINCT用在聚合函数内部时,会先对目标列去重再计算。COUNT(DISTINCT product_id)可以统计出现过多少种产品,SUM(DISTINCT product_id)在逻辑上很少使用,因为产品ID求和通常没有业务含义,但某些场景下需要统计不同数值的唯一和。AVG(DISTINCT price)只对不同的价格做平均,忽略重复价格,这与AVG(price)的含义完全不同。DB2还支持COUNT_BIG函数,返回DECIMAL(31,0)类型,适合超大表行数统计,避免普通INT计数溢出。

-- 创建示例表
CREATE TABLE sales (
    sale_id INT NOT NULL,
    product_id INT,
    quantity INT,
    price DECIMAL(10,2)
);

INSERT INTO sales VALUES
(1, 101, 5, 20.00),
(2, 102, NULL, 30.00),
(3, 101, 3, 20.00),
(4, NULL, 2, 15.00);

-- 基础聚合与NULL处理
SELECT
    COUNT(*)               AS total_rows,
    COUNT(quantity)        AS non_null_quantity,
    SUM(quantity)          AS total_quantity,
    AVG(quantity)          AS avg_quantity,
    SUM(price)             AS total_price
FROM sales;

-- DISTINCT 去重聚合
SELECT
    COUNT(DISTINCT product_id) AS distinct_products,
    SUM(DISTINCT product_id)    AS sum_distinct_product_id,
    AVG(DISTINCT price)         AS avg_distinct_price
FROM sales;

上述SQL中,SUM(quantity)返回8,因为NULL行被忽略,COUNT(quantity)为3,而COUNT(*)为4。DISTINCT示例中,COUNT(DISTINCT product_id)结果为2,因为101重复出现,NULL product_id不参与计数。SUM(DISTINCT product_id)是101+102=203,AVG(DISTINCT price)只对20.00、30.00、15.00三个不同价格求平均。理解这些细节后,再遇到统计值与直觉不符的问题,可以快速定位是不是NULL或重复值在起作用。

二、窗口函数:让SUM和AVG具备行级计算能力

传统GROUP BY会把结果压缩到分组粒度,丢失明细行信息。DB2支持OLAP窗口函数,可以在保留每一行数据的同时,对分区内的行进行聚合。语法为聚合函数 OVER (PARTITION BY 分组列 ORDER BY 排序列 ROWS/RANGE 窗口范围)。SUM函数配合ORDER BY和ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,可以实现分区内累计求和。例如按部门对员工工资排序后,计算截至当前员工的累计工资,不需要自连接或游标。

AVG的窗口用法非常实用:通过ROWS BETWEEN 2 PRECEDING AND CURRENT ROW可以计算当前行及前两行的移动平均,适合做趋势平滑。COUNT(*) OVER (PARTITION BY dept_id)能直接给出每个部门的员工总数,并出现在每一行上,方便后续计算占比。窗口函数与普通GROUP BY的最大区别在于:窗口函数不减少结果集行数,聚合结果作为新列附加到原有行上。它也不会导致分组内多行合并,因此常用于排名、累计、环比等分析。

SELECT
    emp_id,
    dept_id,
    salary,
    SUM(salary) OVER (PARTITION BY dept_id ORDER BY emp_id
                      ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_salary,
    AVG(salary) OVER (PARTITION BY dept_id ORDER BY emp_id
                      ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg_3,
    COUNT(*) OVER (PARTITION BY dept_id) AS dept_emp_count
FROM employee
ORDER BY dept_id, emp_id;

在上例中,running_salary按emp_id顺序累计,每一行都能看到当前累计值;moving_avg_3是最近三行的工资均值;dept_emp_count则显示部门总人数。需要注意窗口范围子句如果省略,默认是RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,在有重复排序键时可能与ROWS行为不同。建议显式写出ROWS范围,确保结果可预测。

三、条件聚合与多层级小计:减少扫描次数的实战技巧

条件聚合是指将CASE WHEN表达式作为聚合函数的参数,从而在一次扫描中同时得到多个条件下的统计值。例如SUM(CASE WHEN salary > 10000 THEN 1 ELSE 0 END)可以统计高薪员工数量,AVG(CASE WHEN salary > 10000 THEN salary END)只对高薪员工求平均。CASE WHEN返回NULL时,SUM、AVG、COUNT会自动忽略,因此不需要额外WHERE过滤。这种写法能避免针对每个条件执行多次查询,大幅减少I/O。

在报表场景中,往往需要同时输出明细汇总、部门小计和总计。如果使用多次GROUP BY再UNION ALL,代码冗长且需要重复扫描。DB2提供GROUPING SETS、ROLLUP和CUBE三种分组扩展语法。GROUPING SETS可以指定多个分组组合,一次查询返回多个层级的结果。ROLLUP会产生从最细粒度到总计的逐级汇总,CUBE则生成所有维度组合的笛卡尔积。配合GROUPING函数可以判断当前行是否属于小计行,便于前端标识。

-- 条件聚合
SELECT
    dept_id,
    COUNT(*)                                          AS total_emp,
    SUM(CASE WHEN salary > 10000 THEN 1 ELSE 0 END)   AS high_salary_count,
    AVG(CASE WHEN salary > 10000 THEN salary END)     AS avg_high_salary,
    SUM(CASE WHEN job_id = 'MANAGER' THEN salary END) AS manager_salary_total
FROM employee
GROUP BY dept_id;

-- GROUPING SETS 多层级小计
SELECT
    dept_id,
    job_id,
    SUM(salary) AS total_salary,
    COUNT(*)    AS emp_count,
    GROUPING(dept_id) AS grp_dept,
    GROUPING(job_id)  AS grp_job
FROM employee
GROUP BY GROUPING SETS (
    (dept_id, job_id),
    (dept_id),
    ()
)
ORDER BY dept_id, job_id;

条件聚合SQL中,高薪员工数量和平均工资仅对salary大于10000的记录计算,其余行被忽略但保留在其他列中。GROUPING SETS示例返回部门与岗位的明细行、部门小计行以及总计行,GROUPING(dept_id)返回1表示该行不是按dept_id分组,而是汇总行。与UNION ALL相比,这种写法让优化器只扫描一次employee表,并通过排序和哈希聚合完成多层级统计,执行计划更加高效。不过ROLLUP和CUBE生成的组合数量可能很大,需要评估维度数量和结果集膨胀。

还需要注意,窗口函数与GROUP BY分组扩展可以结合使用,但执行顺序不同。DB2会先执行FROM、WHERE、GROUP BY和HAVING,之后才计算窗口函数。因此窗口聚合不能直接引用SELECT别名,也不应该在WHERE中过滤窗口函数结果。如果需要按累计值过滤,应当将窗口查询作为子查询或CTE,再在外层使用WHERE条件。理解了这些原理后,就能在复杂统计场景中灵活组合SUM、AVG、COUNT,写出高效且可维护的DB2查询。

DB2聚合函数SUM函数AVG函数修改时间:2026-09-18 11:42:06

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0918/58807.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。