where筛行、having筛组是本质区别:where在group by前执行,不可用聚合函数;having在group by后执行,必须配合分组(mysql特例除外);where提前过滤提升性能,having延迟过滤导致低效。

WHERE 筛的是行,HAVING 筛的是组——这是唯一需要记住的本质。其他所有表现(报错、性能差、结果不对)都从这个事实自然推导出来。
WHERE 为什么不能用 COUNT(*) 或 SUM(price)?
因为数据库根本还没算出这些值。执行 WHERE 阶段时,数据还是一条一条的原始记录,COUNT(*) 还不存在,语法直接拒绝。
- ✅ 合法:
WHERE order_date > '2025-01-01'(单行字段可直接判断) - ❌ 报错:
WHERE SUM(amount) > 1000(此时SUM尚未生成) - ⚠️ 常见错觉:加了
GROUP BY就能让WHERE用聚合函数——不行,执行顺序固定,WHERE永远早于GROUP BY
HAVING 必须配合 GROUP BY 吗?
逻辑上必须,语义上也应如此。虽然 MySQL 允许 SELECT COUNT(*) FROM users HAVING COUNT(*) > 100 这种写法(把整张表当一个组),但它是方言特例、不可移植、易引发误解。
- ✅ 推荐写法:
SELECT dept, COUNT(*) AS cnt FROM emp GROUP BY dept HAVING cnt > 3(显式分组,意图清晰) - ❌ 避免写法:
SELECT COUNT(*) FROM users HAVING COUNT(*) > 100(无GROUP BY,SQL 标准不鼓励) - ? 注意:
HAVING可引用SELECT中定义的聚合别名,比如cnt,这是合法且常见用法
WHERE 和 HAVING 混用时,性能怎么差出一个数量级?
关键在数据量缩减时机:WHERE 能让数据库跳过大量原始行;HAVING 则必须先把所有分组和聚合全算完,再扔掉不满足条件的组。
- ✅ 高效:
WHERE order_year = 2025先筛掉 90% 历史订单,再GROUP BY customer_id聚合,最后HAVING SUM(amount) > 50000 - ❌ 低效:把
order_year = 2025放进HAVING,数据库得先对全部年份做分组求和,内存爆、IO 多、耗时长 - ? 查看执行计划时,注意
rows_examined:WHERE 优化后该值应显著下降;HAVING 前置则毫无改善
最容易被忽略的点是:WHERE 和 HAVING 不是“选哪个更顺手”的问题,而是“数据库根本不允许你乱换位置”的硬性流程约束。写错不是结果不准,是直接报错或隐式降级——比如把本该 WHERE 的条件塞进 HAVING,查询可能跑通,但性能已悄然崩坏,而你毫无察觉。











