WHERE在分组前过滤,HAVING必须等聚合完成;WHERE处理原始行且能用索引,HAVING只能对聚合结果筛选且无法利用索引;误将非聚合条件放HAVING会导致全量分组,性能崩坏。

WHERE在分组前过滤,HAVING必须等聚合完成
因为数据库执行顺序是固定的:FROM → WHERE → GROUP BY → HAVING → SELECT。WHERE处理的是原始行,能直接跳过不满足条件的数据;而HAVING只能等GROUP BY把所有行分好组、再逐组算完COUNT(*)、SUM()等值之后才开始工作——中间结果集有多大,它就得扫多大。
WHERE能用索引,HAVING基本不能
WHERE中对order_date > '2025-01-01'这类条件,只要该字段有索引,就能快速定位;HAVING里的COUNT(*) > 5或SUM(amount) > 10000,数据库无法下推到存储层,只能全量计算后再筛。即使amount字段有索引,HAVING SUM(amount) > 10000也用不上。
常见误放条件导致性能崩坏
把本该写在WHERE里的非聚合条件错塞进HAVING,是最典型的慢查询根源:
-
HAVING status = 'paid'——status是单行字段,应改用WHERE status = 'paid' -
HAVING create_time > '2025-01-01'—— 同样应前置,否则所有年份数据都参与分组 -
HAVING user_id IN (1,2,3)—— 若user_id未出现在GROUP BY中,多数引擎会报错或返回空
GROUP BY字段没索引时,HAVING再简单也救不回来
如果GROUP BY dept, region,但表上没有(dept, region)复合索引,数据库大概率触发Using temporary; Using filesort,此时HAVING哪怕只写HAVING 1=1也会很慢。检查EXPLAIN输出中的type是否为ALL,以及Extra是否有Using filesort。
HAVING本身,而是它被迫处理的那一大坨中间结果——而这些结果,本可以在WHERE阶段就被砍掉。

















