sql执行顺序为from→where→group by→having→select→order by;where过滤原始行,having筛选分组后结果,二者作用对象不同且不可互换。

WHERE在GROUP BY前执行,HAVING在GROUP BY后执行
数据库实际执行顺序是 FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY,不是你写的顺序。这意味着 WHERE 看到的是原始表里一条条未分组的行;而 HAVING 看到的是已经按 GROUP BY 分好、聚合函数(如 COUNT(*)、SUM(amount))全部算完的结果集。
常见错误现象:
-
WHERE COUNT(*) > 5直接报错——因为此时连分组都没开始,COUNT(*)根本没意义 -
HAVING order_date > '2025-01-01'多数数据库报错或返回空——除非order_date在GROUP BY列表中,否则它不属于“组”的属性
WHERE过滤行,HAVING过滤组
这是最核心的语义区别,不是性能差异,而是作用对象完全不同。
-
WHERE status = 'active':把所有status不是'active'的记录直接丢掉,它们不会参与后续任何分组或聚合 -
GROUP BY dept HAVING AVG(salary) > 8000:先按部门分组、算出每个部门的平均工资,再整组扔掉平均工资 ≤ 8000 的部门——哪怕该部门有 200 人,只要不满足条件,整个组消失
别名能不能用,取决于执行时机
WHERE 不能用 SELECT 中定义的别名,HAVING 可以——这不是语法偏好,而是执行顺序决定的。
-
SELECT user_id, COUNT(*) AS cnt FROM logs GROUP BY user_id HAVING cnt > 10✅ 合法,cnt在SELECT阶段已定义,HAVING在其后执行 -
SELECT user_id, COUNT(*) AS cnt FROM logs WHERE cnt > 10❌ 报错,WHERE在SELECT前执行,cnt还不存在
不写GROUP BY时HAVING的行为不可靠
MySQL 允许 SELECT COUNT(*) FROM users HAVING COUNT(*) > 100 这种写法,但它是方言特例,不是标准 SQL 行为。
- PostgreSQL、SQL Server、Oracle 等会直接报错:
HAVING without GROUP BY - 即使在 MySQL 中,这种写法也掩盖了逻辑意图:你是想查整张表的聚合结果?还是忘了加
GROUP BY? - 生产环境应避免,明确写出
GROUP BY才能保证可读性与跨库兼容性
真正容易被忽略的点是:执行顺序不是“优化技巧”,而是 SQL 引擎的硬性阶段划分。一旦混淆 WHERE 和 HAVING 的作用时机,就不是结果不准的问题,而是语法报错或逻辑完全错位——比如把时间范围条件塞进 HAVING,等于让数据库对五年数据全量分组求和,再扔掉四年的结果。










