HAVING必须跟在GROUP BY后,用于分组后过滤组;WHERE在分组前过滤行,不支持聚合函数,执行顺序为WHERE→GROUP BY→HAVING。

HAVING 必须跟在 GROUP BY 后面,不能替代 WHERE
WHERE 在分组前过滤行,HAVING 在分组后过滤组。如果把本该写在 HAVING 的条件错写进 WHERE,比如想筛出“平均工资 > 5000”的部门却写了 WHERE AVG(salary) > 5000,会直接报错:ERROR: aggregate functions are not allowed in WHERE。因为 WHERE 执行时还没分组,更没算出 AVG()。
正确顺序只能是:GROUP BY → 聚合计算 → HAVING 判断每组是否保留。
HAVING 只能用分组字段或聚合函数
在 HAVING 子句里出现的列,要么是 GROUP BY 中明确列出的字段(如 department),要么是聚合表达式(如 COUNT(*)、MAX(created_at))。写 HAVING name = 'Alice' 是非法的——除非 name 在 GROUP BY 里,否则这组里可能有多个 name,数据库不知道你指哪个。
-
HAVING COUNT(*) >= 3✅ 合法:统计每组行数 -
HAVING department = 'HR'✅ 合法:前提是GROUP BY department -
HAVING salary > 8000❌ 非法:未聚合也未分组,语义模糊 -
HAVING AVG(salary) > 8000✅ 合法:聚合后比较
WHERE + GROUP BY + HAVING 的执行顺序很关键
实际执行不是从左往右读,而是严格按逻辑阶段:先 WHERE 拿出原始数据子集,再 GROUP BY 分组,最后 HAVING 筛组。这意味着:
- 加
WHERE status = 'active'能显著减少分组数据量,比全量分组再用HAVING过滤更高效 -
HAVING无法访问被WHERE过滤掉的行——哪怕那行本能让某组满足条件 - 如果需要对聚合结果再加工(比如取 top 3 组),
HAVING做不到,得靠窗口函数或子查询
示例:查“2024 年入职且平均薪资超 6000 的部门”
SELECT department, AVG(salary) AS avg_sal FROM employees WHERE YEAR(hire_date) = 2024 GROUP BY department HAVING AVG(salary) > 6000;
NULL 和空组容易被 HAVING 意外过滤
GROUP BY 遇到 NULL 值会单独成一组,但如果你在 HAVING 里用了涉及该字段的非聚合判断(比如 HAVING department IS NOT NULL),它其实不会生效——因为 department 是分组键,这一组本身就代表 department = NULL。真正要排除空部门组,得在 WHERE 阶段提前干掉:WHERE department IS NOT NULL。
另一个陷阱:当分组结果为空(比如 WHERE 过滤后没剩任何行),整个 GROUP BY 查询返回零行——HAVING 根本没机会运行。别指望它返回一个“空组满足条件”的假结果。
复杂点在于,有些数据库(如 MySQL 5.7 默认)允许 SELECT 列表中出现未分组、非聚合字段,这时 HAVING 行为可能和预期不一致,务必确认 SQL 模式是否启用 ONLY_FULL_GROUP_BY。

















