#21 SQL 简单

HAVING 和 WHERE 的区别是什么?

过滤聚合

🎯 考察意图

这是 SQL 基础但极易写错的题。考察候选人是否理解「先过滤行、再分组、再过滤组」的逻辑执行顺序,以及能否在业务聚合里把条件放到正确的子句,避免「聚合写进 WHERE」这种硬伤。

📐 回答框架

  1. 先给一句话:WHERE 过滤行,HAVING 过滤分组。
  2. 补执行顺序:FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY。
  3. 举业务例子:先筛有效订单,再按用户汇总,再留下消费超过阈值的用户。
  4. 收尾讲性能与别名:能 WHERE 就别 HAVING;SELECT 别名通常不能给 WHERE 用。

💬 参考答案(口述版)

一句话:WHERE 在分组前按行过滤;HAVING 在分组后按组过滤。所以「某行满不满足条件」用 WHERE,「某一组的汇总满不满足条件」用 HAVING。

逻辑执行顺序可以记成:FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY。WHERE 看不到聚合结果,因此 WHERE COUNT(*) > 1 会直接报错或语义非法;这类条件必须放到 HAVING。

业务例子:统计「有效订单金额 ≥ 1000 的用户」。
1. 先用 WHERE 丢掉退款/测试单,减少参与聚合的行
2. 再 GROUP BY user_id 求每人金额
3. 最后 HAVING SUM(amount) >= 1000 留下达标的组

SELECT user_id, SUM(amount) AS gmv
FROM orders
WHERE status = 'paid'
GROUP BY user_id
HAVING SUM(amount) >= 1000;

性能上:WHERE 越早砍行,扫描和 shuffle 越小;非聚合条件不要偷懒写进 HAVING。HAVING 不跟 GROUP BY 时,整张结果被当成一组,常用来写「全表汇总后再判断」的查询。

🔍 关键词

行过滤组过滤执行顺序GROUP BY聚合条件

💭 面试官可能的追问

追问 1:能不能只用 HAVING、不用 WHERE?

语法上有时能跑通,但不该这么写。非聚合过滤放 HAVING,等于先把不该参与分组的行也聚合进去,再丢掉整组或整表,既慢又容易把口径算错。能在行级排除的,一律 WHERE。

追问 2:HAVING 里能用 SELECT 的别名吗?比如 HAVING gmv >= 1000?

标准语义里 HAVING 发生在 SELECT 之前,别名还没诞生,多数引擎要重复写表达式:HAVING SUM(amount) >= 1000。MySQL 等方言可能放行别名,但面试和跨引擎代码应当它不存在,避免换仓就挂。

⚠️ 常见踩坑

  • 把 COUNT/SUM 条件写进 WHERE,引擎直接拒绝或语义错
  • 行级条件(状态、日期、类型)却写在 HAVING,丢掉前期剪枝
  • 以为 HAVING 一定能用 SELECT 别名,换引擎后报错

🧠 记忆锚点

WHERE 在分组前砍行;HAVING 在分组后砍组。