where筛选行、having筛选组,where在group by前执行故不能用聚合函数,having必须跟在group by后且只能引用分组字段或聚合结果,where早过滤可大幅提升性能。

WHERE 筛的是行,HAVING 筛的是组——写错位置不仅查不出结果,还可能拖慢查询几倍。
WHERE 不能用 COUNT()、SUM() 这类聚合函数
因为 WHERE 在 GROUP BY 之前执行,此时数据库还没把数据分组,更没算出任何聚合值。你让它判断 COUNT(*) > 5,它根本不知道“这个 *”属于哪一组。
- ✅ 正确:
WHERE status = 'active'(单行字段,没问题) - ❌ 报错:
WHERE COUNT(*) > 5(语法错误,多数数据库直接拒绝执行) - ⚠️ 常见误写:
SELECT user_id FROM orders GROUP BY user_id WHERE SUM(amount) > 1000—— 这里WHERE放错位置,且用了聚合,必然失败
HAVING 必须跟在 GROUP BY 后面,且字段受限
HAVING 看的是分组后的“结果集”,所以它能用 SUM()、COUNT(),但不能随便引用原始表里的任意字段。
- ✅ 正确:
HAVING COUNT(*) > 5(聚合值,合法) - ✅ 正确:
HAVING AVG(price) > 100(聚合值,合法) - ❌ 报错:
HAVING product_id = 123(除非product_id在GROUP BY或SELECT中出现,否则提示Unknown column) - ? 小技巧:如果想按某个字段过滤分组,得先把它放进
GROUP BY,比如GROUP BY category, product_id HAVING product_id = 123
性能差一截:WHERE 能早过滤,就别等 HAVING
WHERE 先剔除 90% 的无效行,后续分组、聚合的数据量就小得多;而 HAVING 是等所有分组和计算做完才动手,CPU 和内存压力大得多。
- ✅ 推荐:
WHERE created_at >= '2025-01-01' GROUP BY user_id HAVING SUM(amount) > 5000(先限时间范围) - ❌ 反模式:
GROUP BY user_id HAVING created_at >= '2025-01-01' AND SUM(amount) > 5000(created_at不在分组字段中,多数库报错;即使支持,也得对全表分组再筛,极慢) - ? 实测差异:千万级订单表上,用 WHERE 先过滤年份比全量跑 HAVING 快 4–7 倍,尤其当聚合字段索引缺失时
WHERE 和 HAVING 可以一起用,但顺序不能乱
真实业务查询往往既要“挑出符合条件的原始行”,又要“从这些行里找出满足聚合条件的分组”。这时必须严格按 SQL 执行顺序来写:FROM → WHERE → GROUP BY → HAVING。
- ✅ 正确示例:
SELECT department, COUNT(*) FROM employees WHERE hire_date > '2022-01-01' GROUP BY department HAVING COUNT(*) >= 3 - ? 注意:WHERE 过滤的是
employees表每一行,HAVING 过滤的是每个department分组的计数结果 - ? 不存在 “WHERE + HAVING 混写同一条件” 的优化捷径——数据库不会自动帮你挪逻辑,写在哪,就在哪执行
最易被忽略的一点:HAVING 单独出现(无 GROUP BY)虽在部分数据库中语法通过,但语义模糊——整张表被当作一个组处理,极易引发误解或迁移风险。生产环境应始终显式写出 GROUP BY。











