聚合与 GROUP BY
COUNT、SUM、AVG、HAVING。
聚合与 GROUP BY
这一课你将能做到
- 用 COUNT / SUM / AVG / MAX / MIN 做全表统计,并用 GROUP BY 做分组统计
- 用 HAVING 过滤分组结果,说清 WHERE 和 HAVING 的执行时点差异
- 把 JOIN + WHERE + GROUP BY + HAVING + ORDER BY 串成一条完整的统计查询
前置:第一课建库脚本已跑(若做过第 6 课的写操作,先重跑脚本复原数据,保证输出一致)。
为什么要学
前面几课查的都是明细行;但业务方要的经常是统计口径:每个分类几门课、每个学生完成几节、各课程卖了多少钱。报表、看板、运营后台的数字,全是聚合查询产出的。聚合也是 SQL 面试的重灾区——COUNT 的三种写法、ONLY_FULL_GROUP_BY、WHERE 与 HAVING 的区别,几乎每轮都会问。
先建立图像
聚合就是「先分堆、再算数」:
17 条学习记录
│ 按 student_id 分堆(GROUP BY)
▼
┌────────┬────────┬────────┬─…─┐
│ s=1(4) │ s=2(2) │ s=3(4) │ … │ ← 每堆是一个「组」
└────────┴────────┴────────┴─…─┘
│ 每堆内 COUNT(*)、AVG(score)
▼
每个学生一行:完成数、平均分
完整逻辑执行顺序(背下来,所有聚合疑问都能用它解释):
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
取表 筛行 分堆 筛组 出结果 排序 截取
跟着做
步骤 1:五种聚合函数,全表一桶
SELECT MAX(price) AS 最贵,
MIN(price) AS 最便宜,
ROUND(AVG(price), 2) AS 均价,
SUM(price) AS 总价,
COUNT(*) AS 课程数
FROM course;
-- 399 / 0 / 149.29 / 1045 / 7
没有 GROUP BY 时,整张表就是一组,结果永远只有一行。
步骤 2:COUNT 的三种写法(本课第一个考点)
SELECT COUNT(*), COUNT(score), COUNT(DISTINCT student_id)
FROM study_record;
-- 17 / 16 / 6
COUNT(*):数行数,17 行COUNT(score):数 score 非 NULL 的行,大鹏那条 score 是 NULL,所以 16COUNT(DISTINCT student_id):去重后 6 个学生学过东西(小吴没学)
三个数字不一样,全都对——关键是你要的是哪个口径。
步骤 3:GROUP BY 单列分组
SELECT category,
COUNT(*) AS course_cnt,
ROUND(AVG(price), 2) AS avg_price
FROM course
GROUP BY category;
-- java 2 249.00
-- database 3 133.00
-- frontend 2 74.00
「每个分类几门课、平均卖多少钱」一句话出来了。SELECT 里非聚合列(category)必须出现在 GROUP BY 中,原因见要点 2。
步骤 4:GROUP BY 多列分组
SELECT category, level, COUNT(*) AS cnt
FROM course
GROUP BY category, level;
-- 6 行:先按 category 分堆,堆内再按 level 分堆