SQL 的聚合函数对一组值做计算并返回单个汇总值,是数据分析的基础工具。最常用的是五个核心函数:COUNT(计数)、SUM(求和)、AVG(平均值)、MIN(最小值)、MAX(最大值)。它们通常与 GROUP BY 配合,把数据按维度分组后再汇总;除 COUNT(*) 外,其余函数默认忽略 NULL 值。据 MySQL 官方文档(Aggregate Function Descriptions)记载,聚合函数作用于”值的集合”,常借助 GROUP BY 把值分成子集;Microsoft Learn 的 T-SQL 教程也明确,聚合函数返回标量值,可在 SELECT、HAVING、ORDER BY 中使用,但不能直接出现在 WHERE 子句里。
一、聚合函数的工作前提
普通查询一次处理一行,返回的行与原表一一对应;聚合函数则”压缩”多行——把一组值收敛成单个结果。据 LeetCode SQL50 的整理,这种”多行进、单行出”的特性决定了三个基本约束:
- 忽略 NULL:除
COUNT(*)外,所有聚合函数跳过NULL值,不参与计数或计算。 - 不能用于 WHERE:行级过滤发生在聚合之前,
WHERE里不能写AVG(salary) > 5000,这种”对结果再过滤”要交给HAVING。 - 非聚合列必须进 GROUP BY:
SELECT中既写了聚合函数又写了普通列时,普通列必须出现在GROUP BY中,否则多数引擎报only_full_group_by类错误。
二、五大核心函数逐一拆解
| 函数 | 作用 | 对 NULL 的处理 | 适用类型 |
|---|---|---|---|
| COUNT(*) | 统计所有行数 | 含 NULL 行也计入 | 任意 |
| COUNT(col) | 统计非 NULL 的列值数 | 忽略 NULL | 任意 |
| SUM(col) | 数值列求和 | 忽略 NULL | 数值 |
| AVG(col) | 数值列平均值(SUM/COUNT) | 忽略 NULL | 数值 |
| MIN(col) | 最小值 | 忽略 NULL | 数值/日期/字符串 |
| MAX(col) | 最大值 | 忽略 NULL | 数值/日期/字符串 |
COUNT:三种写法含义不同
SELECT COUNT(*) AS total_rows, -- 统计所有行,含 NULL
COUNT(bonus) AS with_bonus, -- 只统计 bonus 非 NULL 的行
COUNT(DISTINCT dept) AS dept_cnt -- 统计不重复的部门数
FROM employees;
COUNT(*) 与 COUNT(1) 在主流引擎(MySQL、SQL Server、PostgreSQL)中结果等价,都是全行计数;COUNT(col) 与 COUNT(DISTINCT col) 才涉及空值与非空去重。leapcell 的指南强调,COUNT(*) 是唯一把 NULL 行也算进去的形式。
SUM 与 AVG:只认数值
SELECT SUM(salary) AS payroll,
AVG(salary) AS avg_salary
FROM employees;
二者都忽略 NULL。要理解 AVG 的本质是 SUM/COUNT(非NULL),因此 AVG(bonus) 的分母是”非 NULL 的 bonus 行数”,而非总行数——这是很多人算错平均值的原因。GeeksforGeeks 与 sqlmastery 均指出,若全部为 NULL,SUM/AVG 返回 NULL 而非 0。
MIN 与 MAX:不限数值
MIN/MAX 不仅支持数字,也支持日期(最早/最晚)和字符串(按排序规则的首/尾)。Microsoft Learn 举例:用 MIN(CompanyName)、MAX(CompanyName) 即可取字母序的首尾公司名,无需额外排序。
SELECT MIN(hire_date) AS earliest,
MAX(hire_date) AS latest
FROM employees;
三、与 GROUP BY 搭配分组汇总
聚合函数真正的威力在分组。下面按部门求平均薪资:
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department;
GROUP BY 先把行按 department 分桶,再在每个桶内跑聚合。MySQL 官方文档说明,没有 GROUP BY 时,整张表被当作一个组;GROUP BY 把”值的集合”切成多个子集分别汇总。
四、HAVING:对聚合结果再过滤
WHERE 过滤行、HAVING 过滤组,二者顺序不同。要筛出”平均薪资超 600 的部门”:
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 600;
Leapcell 用同一例子说明:WHERE 无法引用聚合结果,必须改用 HAVING——它作用于分组之后的汇总值。
五、进阶:条件聚合与扩展函数
条件聚合(CASE + 聚合)
把行级判断嵌进聚合,一行得出多维度计数:
SELECT COUNT(*) AS total,
SUM(CASE WHEN status = 'approved' THEN 1 ELSE 0 END) AS approved,
SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END) AS rejected
FROM orders;
LeetCode SQL50 把此列为高频技巧:无需多次 GROUP BY,单条语句得到各状态数量。
字符串拼接与统计类
MySQL 提供 GROUP_CONCAT()(PostgreSQL/SQL Server 对应 STRING_AGG())把分组内的文本拼成一行;STDDEV()/VARIANCE() 给出标准差与方差,用于衡量数据离散程度。MySQL 9.0 文档的函数表中还列有 BIT_AND/BIT_OR、JSON_ARRAYAGG/JSON_OBJECTAGG 等专用聚合,覆盖位运算与 JSON 汇总场景。
窗口化能力
多数聚合函数在 MySQL 8.0+、SQL Server、PostgreSQL 中可加 OVER() 转为窗口函数,在不折叠行的前提下完成分组计算,这是它在”聚合”与”窗口函数”两类用法间的桥梁。
六、使用聚合函数的执行步骤
- 明确要算的指标(计数、求和、均值、极值),选对函数。
- 确定分组维度,写
GROUP BY;非聚合列必须进GROUP BY。 - 处理空值:确认
COUNT(*)与COUNT(col)的语义差异,避免均值分母算错。 - 用
WHERE先过滤行,再GROUP BY分组;对结果过滤用HAVING。 - 需要单 SQL 多维度统计时,用
CASE WHEN做条件聚合。
常见问题(FAQ)
COUNT(*) 和 COUNT(1) 有区别吗?
主流引擎结果相同,都是统计所有行(含 NULL);差异仅在上古版本的微观实现,现代优化器已等价。
AVG 算出来不对,为什么?
它只除以非 NULL 值的个数,忽略 NULL 行。含空值时分母比你预期的小,需先确认数据。
聚合结果能用 WHERE 过滤吗?
不能,WHERE 在聚合前执行。对汇总值过滤必须用 HAVING。