WHERE过滤行、HAVING过滤分组结果:WHERE在GROUP BY前执行,作用于原始行且不可用聚合函数;HAVING在GROUP BY后执行,作用于分组结果且必须依赖聚合或分组字段。

WHERE 过滤行,HAVING 过滤分组结果
这是最本质的区别:WHERE 在 GROUP BY 之前执行,作用对象是原始数据的每一行;HAVING 在 GROUP BY 之后执行,作用对象是已经分组并计算完聚合函数(如 COUNT()、SUM())的结果集。
- 写
WHERE COUNT(*) > 10会报错 —— 因为COUNT(*)还没算出来,行都还没分组 - 写
HAVING COUNT(*) > 10是合法的 —— 分组完成,每组一个计数,可以筛掉人数 ≤10 的组 -
WHERE排除的行,不参与后续分组;HAVING排除的是整组,不影响其他组
WHERE 不能用聚合函数,HAVING 必须依赖聚合或分组字段
你不能在 WHERE 中写 WHERE AVG(salary) > 8000,MySQL 会直接拒绝解析。而 HAVING 不仅允许,而且通常就靠它来实现“哪些部门平均工资超 8k”这类需求。
- 正确示例:
SELECT dept, AVG(salary) AS avg_sal FROM emp GROUP BY dept HAVING avg_sal > 8000;
- 错误写法:
SELECT dept, AVG(salary) AS avg_sal FROM emp WHERE avg_sal > 8000 GROUP BY dept;
(avg_sal是别名,且出现在WHERE中,双重非法) -
HAVING可以用别名(如avg_sal),WHERE不行 —— 因为SELECT列别名在WHERE阶段还不可见
性能差异:WHERE 能走索引,HAVING 基本不走
如果过滤条件字段上有索引,放在 WHERE 中大概率能命中索引,大幅减少扫描行数;而 HAVING 是对内存中已分组的结果再筛,无法利用底层表索引。
- 想查 “2024 年入职且部门平均薪资 > 15k 的部门”,应把
WHERE hire_date >= '2024-01-01'放前面,先缩小数据集 - 若错误地写成
HAVING hire_date >= '2024-01-01',不仅语法错(hire_date未在GROUP BY中也不聚合),更关键的是:即使能运行,也会全表扫完再分组,再筛 —— 白耗资源 - 关联查询中,
WHERE先筛再 join,HAVING是 join 后再筛,前者效率通常高得多
执行顺序决定能否混用 —— 不是“选一个”,而是“各司其职”
标准 MySQL 查询执行顺序是:FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT。这意味着你经常需要 WHERE 和 HAVING 同时出现,各自干好自己的活。
- 典型组合场景:
SELECT dept, COUNT(*) AS cnt, AVG(salary) FROM emp WHERE status = 'active' AND salary > 3000 GROUP BY dept HAVING cnt >= 5 AND AVG(salary) > 12000 ORDER BY cnt DESC;
- 这里:
WHERE确保只统计在职且底薪够高的员工(影响分组基数);HAVING确保只返回人数 ≥5 且平均薪资 >1.2w 的部门(影响最终输出行数) - 漏掉
WHERE可能导致无效数据进分组;漏掉HAVING可能返回大量无业务意义的分组结果
GROUP BY 却写 HAVING,虽然某些 MySQL 版本允许(视为对整个结果集做一次隐式分组),但语义模糊、可读性差、迁移风险高 —— 生产环境应避免。










