WHERE过滤行,HAVING过滤组;执行顺序为FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY,WHERE作用于原始行、可用原始字段,HAVING作用于分组后结果、必须用聚合函数或GROUP BY字段。

WHERE 过滤行,HAVING 过滤组——执行顺序不同直接决定了能不能用、该不该用、用了会不会慢。
WHERE 和 HAVING 的真实执行时机
数据库引擎处理 SQL 时,并不按你写的顺序执行,而是固定走:FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY。这个链条不可跳过、不可颠倒。
WHERE 看到的是原始表里每一行,比如 status = 'active' 会把整行剔除,这些数据根本不会进后续分组;HAVING 看到的是已经分好组、且每组的 COUNT(*)、AVG(salary) 都算完的结果集。
- 误把
WHERE order_date >= '2026-01-01'写成HAVING,数据库会先对全部年份的数据分组求和,再扔掉 2026 年之前的组——计算白做了 -
WHERE AVG(salary) > 8000必报错,因为AVG此时还没算,逻辑上不存在 -
GROUP BY dept HAVING AVG(salary) > 8000才合法:分组完成,每组平均值已知
哪些条件必须放 WHERE,哪些只能放 HAVING
判断依据不是“想不想”,而是“能不能”——取决于该条件依赖的数据是否已存在。
- 能用原始字段(如
user_id、created_at、status)或普通表达式(如price > 100)的,一律放WHERE - 涉及聚合函数(
COUNT(*)、SUM(amount)、AVG(score))的,只能放HAVING -
HAVING中若引用非聚合字段(如HAVING hire_date > '2020-01-01'),多数数据库会报错,除非该字段出现在GROUP BY或SELECT中 -
HAVING不能用SELECT别名(如cnt),PostgreSQL/SQL Server/Oracle 均不认;MySQL 默认允许但开启ONLY_FULL_GROUP_BY后也会失败
性能影响:早过滤比晚过滤省得多
WHERE 减少输入量,HAVING 只减少输出量——中间所有分组、聚合、内存排序照常进行。
- 查“2026 年下单超 5 次的 VIP 用户”,
WHERE order_date >= '2026-01-01' AND is_vip = 1要写在GROUP BY前,否则全量数据先分组再筛,IO 和 CPU 白耗 - 没有“
HAVING更快”的场景,所谓快,基本是缓存命中、小表误判或语法错误后换写法导致的假象 - MySQL 8.0 虽有简单
HAVING下推优化,但不可控、不可验证;而WHERE走索引是稳定可EXPLAIN的行为
HAVING 单独使用是否合法?
绝大多数生产环境应避免:HAVING COUNT(*) > 10 不加 GROUP BY,在 PostgreSQL、SQL Server、Oracle 中直接报错;MySQL 旧版可能允许,但等价于把整张表聚合成单组,极易引发误解。
实际业务中几乎不会需要“全表聚合后筛选”,真要查总数是否超阈值,直接 SELECT COUNT(*) FROM table 更清晰安全。
最容易被忽略的一点:有些 ORM 或 BI 工具生成的 SQL 会漏掉 GROUP BY,导致 HAVING 失效或报错,上线前务必人工核对语句结构。

















