讲解
GROUP BY 把行聚成组之后,常常还要对「组」做筛选:哪些城市的学生超过 1 人?哪些学生消费超过 100 元?这些条件判断的是聚合结果,而 WHERE 在分组之前就执行了,那时还没有 COUNT() 可言——所以 SQL 提供了 HAVING,专门在分组聚合之后对组做筛选:GROUP BY city HAVING COUNT() > 1。
WHERE 和 HAVING 的分工是本章的核心:WHERE 筛「行」,发生在分组之前;HAVING 筛「组」,发生在分组之后。同一条查询里可以同时有两者:先 WHERE 排除不需要的行,再 GROUP BY 分组,最后 HAVING 留下符合条件的组。完整执行顺序是 FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY。
一个实践建议:能用 WHERE 表达的条件不要放进 HAVING。「已支付订单按学生分组,只看消费超过 100 的」里,status = 'paid' 是行级条件该放 WHERE,SUM(amount) > 100 是组级条件放 HAVING。把行级条件放进 HAVING 不仅语义混乱,还会让数据库先对全部数据分组再过滤,性能更差。
示例
学生人数超过 1 人的城市(注意:城市为 NULL 的学生也聚成一组,会一并出现在结果里):
SELECT city, COUNT(*) AS student_count FROM students GROUP BY city HAVING COUNT(*) > 1;
WHERE 与 HAVING 配合:只看已支付订单,找出消费超过 50 元的学生:
SELECT student_id, SUM(amount) AS paid_total FROM orders WHERE status = 'paid' GROUP BY student_id HAVING SUM(amount) > 50 ORDER BY paid_total DESC;
订单数不少于 2 种的状态(对组级计数筛选):
SELECT status, COUNT(*) AS cnt FROM orders GROUP BY status HAVING COUNT(*) >= 2;
常见坑
- 在 WHERE 里用聚合函数:WHERE COUNT(*) > 1 是语法错误,聚合结果只能用 HAVING 筛。报错信息看不懂时先检查聚合函数的位置。
- 把行级条件挪进 HAVING:HAVING status = 'paid' 语法上有时能跑(分组键恰好包含该列时),但语义和性能都不对。行级条件永远优先 WHERE。
- HAVING 里没有 GROUP BY:单独写 HAVING 时整张表被视为一个组,几乎总是写错了。看到 HAVING 先确认 GROUP BY 在不在。
- 以为 HAVING 能引用任意列:HAVING 里只能用分组键和聚合表达式,引用未分组列和 GROUP BY 章节讲的是同一条铁律。
小结
WHERE 筛行(分组前)、HAVING 筛组(分组后);能放 WHERE 的条件别放 HAVING。下一节进入多表世界:INNER JOIN。