📄 一句话概括
HAVING 对【分完的堆】提条件——WHERE 筛行、HAVING 筛堆;两者执行顺序是本节的灵魂题。
📝 本节和前面几节的关系
3-18 学会分堆聚合;本节补最后一块拼图:【只留人数 2 以上的班】【只留平均分 80 以上的班】——条件写在堆上,WHERE 干不了,HAVING 上场。
一、为什么 WHERE 干不了这活
想查【人数在 2 人以上的班】:
-- 报错!
SELECT class, COUNT(*)
FROM students
WHERE COUNT(*) >= 2 -- ✗ WHERE 执行时还没分堆,COUNT(*) 不存在
GROUP BY class;
顺序问题:WHERE 在分堆之前跑——那时每一行还是一行,【每堆多少人】这个数还没出生,WHERE 没法判断。
HAVING 在分堆之后跑——它看的正是【堆的聚合值】:
SELECT class, COUNT(*)
FROM students
GROUP BY class
HAVING COUNT(*) >= 2;
class COUNT(*)
1 2
2 2
3 班(1 人)被 HAVING 滤掉——筛的是堆,不是行。
二、WHERE 与 HAVING 的分工表
| WHERE | HAVING | |
|---|---|---|
| 筛什么 | 行(分堆前) | 堆(分堆后) |
| 能用聚合函数吗 | ✗(还没聚合) | ✓(这正是它的用途) |
| 位置 | GROUP BY 前 | GROUP BY 后 |
两者可以同时上场:
-- 有分数的行 → 按班分堆 → 只留平均分 70 以上的班
SELECT class, AVG(score) AS 平均
FROM students
WHERE score IS NOT NULL
GROUP BY class
HAVING AVG(score) >= 70;
执行流水线:先筛行(钱七出局)→ 分堆 → 每堆聚合 → 筛堆(3 班出局)。
三、动手 Demo:完整的查询流水线
-- 准备块(没建表先跑 3-6 的准备块)
-- ① 筛堆:人数 2 以上的班
SELECT class, COUNT(*) AS 人数
FROM students GROUP BY class HAVING COUNT(*) >= 2;
-- ② 筛堆:平均分 70 以上的班
SELECT class, AVG(score) AS 平均 FROM students GROUP BY class HAVING AVG(score) >= 70;
-- ③ WHERE + HAVING 连打
SELECT class, COUNT(*) AS 人数
FROM students
WHERE score IS NOT NULL
GROUP BY class
HAVING COUNT(*) >= 1;
②的输出:1 班 88.5、2 班 71.0(3 班无平均值被滤)。 ③的输出:1 班 2 人、2 班 2 人(3 班钱七被 WHERE 先筛走,堆里 0 人被 HAVING 滤掉)。
看到什么算成功:能对着③说出流水线四步【筛行→分堆→聚合→筛堆】各干掉了谁。
四、30 秒实验
把 3-18 30 秒实验的【销量前 10】升级成真业务:【销量 2 以上的班级再按人数排】:
SELECT class AS 商品, COUNT(*) AS 销量
FROM students
GROUP BY class
HAVING COUNT(*) >= 2
ORDER BY 销量 DESC;
——WHERE 筛行 → GROUP BY 分堆 → HAVING 筛堆 → ORDER BY 排队,查询四段论齐了。
五、常见误区
- "HAVING 和 WHERE 差不多,随便用" —— 完全两码事:WHERE 筛行、HAVING 筛堆;条件涉及聚合值(COUNT/AVG…)只能 HAVING。
- "WHERE 里能写 COUNT(*)" —— 报错:WHERE 执行时聚合还没发生。
- "能用 HAVING 的都用 HAVING" —— 反了:能放 WHERE 的条件放 WHERE(分堆前先筛掉,少算很多数据);HAVING 只收【必须看聚合值】的条件。
自测三问(点开对答案)
1. WHERE 和 HAVING 各筛什么? **WHERE 筛行(分堆前)、HAVING 筛堆(分堆后)**。 2. 为什么 WHERE 里不能写 COUNT(*)? **WHERE 先于分堆聚合执行,那时聚合值不存在**。 3. 一条查询的完整执行顺序? **WHERE 筛行 → GROUP BY 分堆 → HAVING 筛堆 → ORDER BY 排队**。- ☐ 下一节(3-20 JOIN-合并多个表获取数据)
相关笔记
- GROUP BY-对记录进行分组 —— 上一节:HAVING 的对象就是那些堆
- 表连接-将各种表组合在一起 —— 下一节起:拆出去的表用 JOIN 拼回来
- 🗄️ 数据库基础 · 索引 —— 全课路线图