SQL HAVING 和 WHERE 的区别
WHERE 过滤进入分组的原始结果行,HAVING 过滤 GROUP BY 和聚合之后的组;核心区别是过滤阶段不同。
#synthesis
#growing
#backend
#sql
#database
[!info] related notes
- 所属 MOC: 数据库 MOC
- 前置概念: SQL GROUP BY, SQL 聚合函数
- 易混淆概念: SQL JOIN 中 ON 与 WHERE 的过滤边界
- 关系笔记: SQL JOIN、GROUP BY 与聚合查询
SQL HAVING 和 WHERE 的区别
一句话结论
WHERE 过滤分组前的行,HAVING 过滤分组和聚合后的组。
逻辑处理位置
用学习心智模型理解:
FROM / JOIN
→ ON
→ WHERE
→ GROUP BY
→ Aggregate
→ HAVING
→ SELECT
→ ORDER BY
→ LIMIT
因此:
WHERE
→ 哪些行有资格参与统计?
HAVING
→ 哪些统计后的组有资格留下?
这描述的是 SQL 的逻辑语义;数据库优化器实际物理执行时可以在保持语义等价的前提下重排操作。
WHERE:先筛行
需求:只统计已支付订单。
SELECT user_id, COUNT(*) AS order_count
FROM orders
WHERE status = 'paid'
GROUP BY user_id;
status = 'paid' 是单行就能判断的条件,未支付订单根本不进入后续分组。
HAVING:再筛组
需求:只保留订单数至少 3 的用户。
SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id
HAVING COUNT(*) >= 3;
COUNT(*) >= 3 只有聚合完成后才有意义,因此属于 HAVING。
两者一起使用
需求:在已支付订单中,找出支付订单数至少 3 的用户。
SELECT user_id, COUNT(*) AS order_count
FROM orders
WHERE status = 'paid'
GROUP BY user_id
HAVING COUNT(*) >= 3;
这里分别是:
WHERE status = 'paid'
→ 定义统计输入
HAVING COUNT(*) >= 3
→ 定义保留哪些统计结果
为什么不要把能提前过滤的条件都塞进 HAVING
如果条件针对原始行,优先在 WHERE 表达通常更清晰,而且让更少的数据进入聚合阶段。即使某些数据库允许某种 HAVING 写法,也不应因此模糊行级条件和组级条件的语义。
与 ON 的区别
ON 是第三个常被混淆的过滤位置:
ON
→ 什么样的左右表行能匹配
WHERE
→ JOIN 后哪些行继续参与后续处理
HAVING
→ 分组聚合后哪些组保留
特别是在 LEFT JOIN 中,把右表条件从 ON 移到 WHERE 可能直接改变结果语义。
最短记忆
WHERE → 筛行
GROUP BY → 分组
HAVING → 筛组