聚合函数有哪五种?
面试常问"SQL 聚合函数有哪五种",标准答案是 COUNT、SUM、AVG、MAX、MIN。但光背名字没用,实际写 SQL 时出问题的地方全在细节上——尤其是 COUNT 和 NULL 的关系,几乎每个人都栽过。
一、COUNT:数行数,但有两种写法
SELECT COUNT(*) FROM orders; -- 统计总行数
SELECT COUNT(id) FROM orders; -- 统计 id 非空的行数
SELECT COUNT(DISTINCT user_id) FROM orders; -- 去重后计数
COUNT(*) 统计所有行,包括字段全为 NULL 的行。COUNT(字段) 只统计该字段非 NULL 的行。表里有 NULL 值时,两者结果会不一样,这是最常见的对不上数的原因。
另外 COUNT(1) 和 COUNT(*) 在主流数据库里执行计划一样,不用纠结性能。
二、SUM:求和,NULL 会被忽略
SELECT SUM(amount) FROM orders WHERE status = 1;
SUM 会跳过 NULL,如果所有值都是 NULL,结果是 NULL 而不是 0。所以前端展示金额时,通常要包一层:
SELECT COALESCE(SUM(amount), 0) FROM orders;
还有一点,SUM 对整数做累加时如果结果超出字段类型范围,MySQL 会报 BIGINT 溢出,大额统计建议先 CAST 成 DECIMAL。
三、AVG:平均值,分母是非 NULL 的行数
SELECT AVG(score) FROM exams;
如果有 10 条记录,其中 2 条 score 是 NULL,AVG 的分母是 8 而不是 10。这和很多人的直觉不同,业务上要算"缺考按 0 分计"时,得用 SUM/COUNT(*) 自己算:
SELECT SUM(score) / COUNT(*) FROM exams;
四、MAX 和 MIN:最大值、最小值
SELECT MAX(price), MIN(price) FROM products;
这两个除了数值,也能用在日期和字符串上。MAX(create_time) 拿最新记录,MIN(create_time) 拿最早记录,比 ORDER BY ... LIMIT 1 更直观(虽然执行效率要看索引)。
注意 MAX 忽略 NULL,如果整列都是 NULL,返回 NULL。
五、五个函数一起用的写法
SELECT
COUNT(*) AS total,
SUM(amount) AS total_amount,
AVG(amount) AS avg_amount,
MAX(amount) AS max_amount,
MIN(amount) AS min_amount
FROM orders
WHERE create_time >= '2026-01-01';
六、配合 GROUP BY 才是真正用法
SELECT user_id, COUNT(*) AS cnt, SUM(amount) AS total
FROM orders
GROUP BY user_id
HAVING SUM(amount) > 1000
ORDER BY total DESC;
这里要记住 WHERE 和 HAVING 的区别:WHERE 在分组前过滤原始行,HAVING 在分组后过滤聚合结果。把聚合条件写进 WHERE 会直接报错。
七、几个真实的坑
- SELECT 里混入非聚合列又没写进 GROUP BY,在 MySQL 的 ONLY_FULL_GROUP_BY 模式下会直接报错,关掉这个模式虽然能跑,但结果是不确定的。
- 聚合函数不能嵌套,SUM(AVG(x)) 这种写法不合法,要用子查询。
- GROUP BY 之后 COUNT(*) 统计的是每组行数,不是总行数,需要总数得另外查。
- 聚合查询在大表上很慢,记得给 GROUP BY 的字段加索引。