SQL HAVING 和 WHERE 的区别
WHERE 过滤原始行,HAVING 过滤分组结果;两者的核心区别在于生效阶段不同。
#type / synthesis
#status / growing
#tech / dev / backend
#resource / sql
#resource / database
[!info] related notes
- 所属 MOC: 数据库 MOC
- 前置概念: SQL, 查询, SQL JOIN、GROUP BY 与聚合查询
- 相关概念: SQL 查询执行流程, SQL 子查询
- 相关主题: 中国移动浙江金华系统开发工程师(Web端)春招复试准备
SQL HAVING 和 WHERE 的区别
一句话结论
WHERE 过滤的是参与分组前的原始行,HAVING 过滤的是 GROUP BY 之后的分组结果。
为什么这两个总被一起问
因为它们看起来都像“加条件”,但语义层级完全不同:
WHERE决定哪些行能进入后续计算HAVING决定哪些组能出现在最终结果里
面试里很多人语法能背出来,但一写到聚合查询就会把条件放错位置。
从查询流程理解
可以先用一个粗略心智模型理解:
FROM/JOINWHEREGROUP BY- 聚合计算
HAVINGSELECTORDER BYLIMIT
这里最关键的是:
WHERE在分组前HAVING在聚合后
所以:
WHERE适合筛原始数据HAVING适合筛统计结果
最小例子
例 1:先筛行,再分组
统计 status = 'paid' 的订单里,每个用户有多少单:
SELECT user_id, COUNT(*) AS order_count
FROM orders
WHERE status = 'paid'
GROUP BY user_id;
这里 WHERE 的意思是:
- 先把未支付订单排除
- 再只对支付成功的订单做分组统计
例 2:先分组,再筛组
查出订单数大于 3 的用户:
SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id
HAVING COUNT(*) > 3;
这里 HAVING 的意思是:
- 先按用户分组
- 再把订单数不超过 3 的组过滤掉
例 3:两者一起出现
查出支付成功订单中,下单数大于 3 的用户:
SELECT user_id, COUNT(*) AS order_count
FROM orders
WHERE status = 'paid'
GROUP BY user_id
HAVING COUNT(*) > 3;
这题里两层条件分别回答两个问题:
WHERE:哪些行参与统计HAVING:哪些统计结果保留
最容易错的地方
1. 把聚合条件写进 WHERE
下面这种写法通常是错的:
SELECT user_id, COUNT(*) AS order_count
FROM orders
WHERE COUNT(*) > 3
GROUP BY user_id;
原因是执行 WHERE 时,分组和聚合结果还没出来。
2. 能用 WHERE 的条件却全塞给 HAVING
例如:
SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id
HAVING status = 'paid';
这类写法语义就不稳。status = 'paid' 这种行级过滤应尽量放在 WHERE,这样:
- 语义更清楚
- 参与分组的数据更少
- 执行代价通常也更合理
3. 以为 HAVING 只能和 GROUP BY 一起出现
多数面试场景里它确实和 GROUP BY 一起讨论,但本质上 HAVING 是对聚合结果加条件。只是如果没有分组,这个场景在业务里没那么常见。
工程上该怎么选
可以直接用一句话判断:
- 条件针对原始行,用
WHERE - 条件针对聚合结果,用
HAVING
如果一个条件本来就能在分组前过滤,优先放到 WHERE,不要无脑推迟到 HAVING。
最短记忆方式
WHERE:先筛行GROUP BY:再分组HAVING:最后筛组