第13周 数据分组统计与子查询
第13周 · 数据分组统计与子查询
上週学了聚合,但只能算"全体的"汇总。这周学 GROUP BY 把数据分组后分别汇总(如每个班各自的平均分),再用 HAVING 筛掉不达标组。另外学子查询——把一个查询塞进另一个查询里,拆解复杂问题。
一、知识点:为什么要分组
AVG(grade) 算的是所有人的平均分。要"每个班的平均分",就得先按班分组,再各组分别聚合——这就是 GROUP BY。
二、操作教程:GROUP BY 分组统计
-- 每个学生(sid)选了几门课
SELECT sid, COUNT(*) AS 门数
FROM sc
GROUP BY sid;
结果:
2024001 2
2024002 1
2024003 2
GROUP BY sid后,SELECT里只能出现:分组列(sid) 或 聚合函数(COUNT/AVG…)。写别的单列会报错。
三、操作教程:HAVING 筛选分组
分组后想"只要选课≥2门的人",用 HAVING(作用于组,不是行):
SELECT sid, COUNT(*) AS 门数
FROM sc
GROUP BY sid
HAVING COUNT(*) >= 2;
结果只保留 2024001、2024003。
四、知识点:WHERE 与 HAVING 的区别(重点)
| WHERE | HAVING | |
|---|---|---|
| 作用对象 | 行(分组前筛选) | 组(分组后筛选) |
| 能否用聚合 | 不能(分组前还没聚合) | 能(HAVING COUNT(*)>=2) |
| 位置 | GROUP BY 之前 |
GROUP BY 之后 |
记忆:先 WHERE 筛掉不要的行 → 再 GROUP BY 分组 → 最后 HAVING 筛掉不要的组。
-- 例:先只算有成绩(grade不为空)的记录,再按sid分组,只要门数>=2
SELECT sid, COUNT(*) AS 门数
FROM sc
WHERE grade IS NOT NULL
GROUP BY sid
HAVING COUNT(*) >= 2;
五、知识点:SQL 执行顺序(背下来)
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
理解这个顺序,很多报错就通了:WHERE 不能用聚合(因为聚合在 GROUP BY 之后才产生);SELECT 的别名不能在 WHERE 里用(因为 SELECT 晚于 WHERE)。
六、知识点 + 操作:子查询
子查询 = 一个 SELECT 嵌在另一个 SELECT 里。口诀:先算内层,再算外层。
6.1 IN 子查询(返回多行)
查"成绩>90 的学生姓名"——先找出哪些 sid 成绩>90,再去 student 取名字:
SELECT sname FROM student
WHERE sid IN (SELECT sid FROM sc WHERE grade > 90);
内层 (SELECT sid FROM sc WHERE grade > 90) 返回 2024001,外层再取该学号姓名 → 张伟。
6.2 标量子查询(返回单个值)
返回一行一列的子查询,可放在 SELECT 或 WHERE 比较里:
-- 查每个学生成绩,并标出"与最高分差多少"
SELECT sid, grade,
(SELECT MAX(grade) FROM sc) AS 最高分
FROM sc;
标量子查询必须只返回 1 个值;返回多值会报错,那种情况改用
IN。
七、综合实操:作业三题
-- 题1:每个学生选课门数
SELECT sid, COUNT(*) AS 门数 FROM sc GROUP BY sid;
-- 题2:选课≥2门的学生
SELECT sid, COUNT(*) AS 门数 FROM sc GROUP BY sid HAVING COUNT(*) >= 2;
-- 题3:成绩>90的学生姓名(IN 子查询)
SELECT sname FROM student
WHERE sid IN (SELECT sid FROM sc WHERE grade > 90);
八、易混点排查表
| 现象 | 解决方法 |
|---|---|
SELECT 写了非分组列报错 |
GROUP BY 后只能选分组列或聚合函数 |
| HAVING 用不了聚合 | 其实能用;不能用聚合的是 WHERE,确认位置 |
| WHERE 里写 COUNT(*) 报错 | 聚合在分组后才产生,筛选组用 HAVING |
| 子查询报"返回多行" | 标量子查询只接受1值;多值改用 IN |
| 子查询结果为空 | 先单独跑内层确认有数据;再套外层 |
| 顺序搞不清 | 背:FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY |
九、小测验(自测)
GROUP BY sid之后,SELECT里能随便写sname吗?为什么?WHERE和HAVING的根本区别是什么?查"平均分>80 的班"该用哪个?- 写出 SQL 执行顺序。为什么
WHERE里不能用聚合函数? - 查"成绩大于90的学生姓名",用 IN 子查询怎么写(先内后外分解)?
【声明】本文由 AI 辅助生成,仅供 MySQL 数据库教学参考,如有疑误以教材为准。
浙公网安备 33010602011771号