HAVING必须跟在GROUP BY后,用于筛选分组后的聚合结果;WHERE作用于分组前的行数据,不可使用COUNT()、AVG()等聚合函数,因其执行时分组尚未发生,聚合值不存在,故MySQL解析阶段即报错。

HAVING 必须跟在 GROUP BY 后面,不能替代 WHERE;想筛聚合结果,就得先分组、再用 HAVING,否则直接报错 Invalid use of group function。
为什么 WHERE 里不能写 COUNT() 或 AVG()
因为 WHERE 执行时,数据还没分组,更没算出 COUNT(*)、AVG(salary) 这些值。MySQL 在解析阶段就拒绝这种写法。
-
SELECT department, COUNT(*) FROM employees WHERE COUNT(*) > 5 GROUP BY department;→ 语法错误 - 正确路径是:先
GROUP BY department,再用HAVING COUNT(*) > 5 - 哪怕你给
COUNT(*)起了别名cnt,WHERE cnt > 5依然非法——WHERE看不见这个别名
HAVING 中能用哪些字段和表达式
HAVING 只能引用两类东西:一是 SELECT 列表中明确出现的列(含别名),二是聚合函数本身;不能引用未分组又未聚合的普通字段。
-
HAVING cnt > 5✅ ——cnt是SELECT里定义的别名 -
HAVING AVG(salary) > 8000✅ —— 聚合函数可直接用,无需别名 -
HAVING hire_date > '2020-01-01'❌ ——hire_date没出现在GROUP BY,也不是聚合结果,MySQL 报错Unknown column 'hire_date' in 'having clause' -
HAVING department = 'tech'✅ ——department在GROUP BY中,属于分组键,允许直接用
WHERE 和 HAVING 混用时,顺序影响性能
执行顺序是 WHERE → GROUP BY → HAVING。把能过滤原始行的条件塞进 WHERE,能显著减少分组输入量。
- 查“2023 年入职且平均薪资 > 10000 的部门”:
SELECT department, AVG(salary) AS avg_sal FROM employees WHERE hire_date >= '2023-01-01' GROUP BY department HAVING avg_sal > 10000; - 如果把
hire_date条件挪到HAVING,MySQL 就得对全部历史数据分组后再筛,慢得多 - 尤其当表有千万级记录、而
WHERE能过滤掉 90% 数据时,性能差距会非常明显
NULL 值在 HAVING 中容易被忽略
聚合函数(如 COUNT()、AVG())默认跳过 NULL,但 HAVING 自身的布尔表达式遇到 NULL 会判为 false,导致整个分组被丢弃。
-
SELECT team, COUNT(*) FROM players GROUP BY team HAVING team IS NULL;—— 只有当team字段真有NULL值且 MySQL 允许其独立成组时才返回结果 -
HAVING AVG(score) > 80—— 如果某组所有score都是NULL,AVG(score)返回NULL,该组不满足条件,被过滤掉 - 这不是 bug,是 SQL 三值逻辑的自然结果,但业务上常被当成“数据丢失”,需提前确认字段是否允许
NULL及其语义
WHERE 还是 HAVING——这取决于它作用的对象:是单行记录,还是整个分组的聚合结果。写错位置,轻则报错,重则查不到数据或查太慢。


















