为什么SQL查询里HAVING子句有时比WHERE子句性能更好

作者:袖梨 2026-07-10
HAVING 从不比 WHERE 性能更好,因为 WHERE 在分组前基于原始行过滤并可利用索引,而 HAVING 必须等待 GROUP BY 完成聚合计算后才执行,无法跳过中间结果。

HAVING 从不比 WHERE 性能更好——这是个常见误解,根源在于混淆了“执行阶段”和“数据量”。

WHERE 和 HAVING 的执行顺序不可逆

SQL 引擎严格按固定顺序执行:FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY。这意味着:

  • WHERE 在分组前过滤原始行,能直接利用索引、跳过大量数据,降低后续计算开销
  • HAVING 必须等 GROUP BY 完成、所有聚合值(如 SUM()COUNT())算出来之后才开始工作,此时数据已分组、内存中存着中间结果集
  • 没有例外:哪怕你写 SELECT 1 HAVING 1=1(无 GROUP BY),多数数据库(如 MySQL 5.7+、PostgreSQL)也把它当作单一分组处理,仍绕不开聚合计算流程

什么情况下会误判 “HAVING 更快”?

实际观察到的“HAVING 查询更快”,几乎都源于对比方式错误或隐藏变量干扰:

  • 拿一个带 WHERE 但没建索引的查询(如 WHERE status = 'pending'),跟一个带 HAVING 且恰好命中缓存的查询比——错把缓存效应当性能优势
  • 在小表(HAVING 看似“没拖慢”,就误以为它“不差”
  • WHERE 写在 GROUP BY 后(语法错误)、或漏写 GROUP BY 导致查询失败,再换成 HAVING 后“能跑了”,就以为是“优化”

真正影响性能的关键不是子句名,而是数据量落点

决定速度的是「在哪一步减少多少数据」:

  • WHERE order_date >= '2026-01-01' 过滤掉 95% 历史订单 → 分组只算最近数据 → 快
  • HAVING COUNT(*) > 10 筛选客户 → 引擎必须先为每个客户算出完整计数 → 即使最终只留 5% 分组,前面 100% 的聚合计算已做完 → 慢
  • 若业务逻辑确实需要聚合后筛选(比如“找出平均响应时间超 2 秒的接口”),那 HAVING 是唯一合法路径,此时谈“优于 WHERE”无意义——它们根本不在同一层面上竞争

最常被忽略的一点:有些数据库(如 MySQL 8.0)在 HAVING 条件简单且字段有索引时,会尝试下推优化,但这属于引擎内部黑盒行为,不可依赖;而 WHERE 的索引走查是稳定、可预期、可 explain 验证的。

相关文章

精彩推荐