where不能使用聚合函数,因其执行在group by之前,此时分组结果尚未生成;having用于筛选分组后的结果,可使用聚合函数和group by字段。

WHERE 无法访问聚合结果,因为执行阶段根本没算出来
SQL 执行顺序是硬性流程:FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY。这意味着 WHERE 阶段看到的还是原始表里一条条未分组的行,COUNT(*)、AVG(salary) 这类值压根不存在。写 WHERE COUNT(*) > 5 不是语法疏忽,而是数据库直接拒绝:它连“有多少行属于同一组”都不知道,怎么可能拿这个数去比。
常见错误现象:
-
ERROR: aggregate functions are not allowed in WHERE(PostgreSQL/SQL Server) -
Invalid use of group function(MySQL)
HAVING 是唯一能对“组”做判断的位置
分组后,每组产出一行摘要(比如“技术部:平均工资 15200,人数 23”),HAVING 就是对这些摘要行筛选。它合法使用的表达式只有三类:
- 出现在
GROUP BY中的列(如department) - 聚合函数(如
COUNT(*)、SUM(amount)) - 它们的组合或常量(如
COUNT(*) * AVG(rating) > 20)
反例:HAVING salary > 8000 报错——salary 是明细字段,每组有多个值,无法直接比较;HAVING user_name = 'Alice' 同样非法,除非 user_name 在 GROUP BY 列表中。
性能上,WHERE 提前砍数据,HAVING 是最后一步筛组
把本该放 WHERE 的条件挪到 HAVING,代价是实打实的:
-
WHERE status = 'paid'能利用索引跳过所有cancelled订单,减少磁盘读和内存分组压力 - 若写成
HAVING status = 'paid',数据库得先把全部订单(含已取消的)全加载、全分组、全聚合,再整组丢弃——IO 和 CPU 白耗 - 关联查询中更明显:
WHERE在JOIN前过滤,HAVING是JOIN完再过滤,中间临时结果集可能膨胀几倍
别名可用性差异暴露执行时机本质
HAVING 能用 SELECT 中定义的别名(如 HAVING avg_sal > 15000),WHERE 一定不能(Unknown column 'avg_sal' in 'where clause')。这不是语法宽容度问题,而是 SELECT 阶段在 HAVING 之后才生成列名,WHERE 根本还没走到那一步。想在 WHERE 里用计算值?只能重复表达式,但聚合函数本身又不被允许——这条路彻底堵死。
真正容易被忽略的点是:混淆 WHERE 和 HAVING 不只是“查得慢”,而是逻辑错位。比如把时间范围条件塞进 HAVING,等于让数据库对五年数据全量分组求和,再扔掉四年的结果——不是优化问题,是执行模型层面的误用。










