HAVING用于分组后筛选聚合结果,必须紧跟GROUP BY之后;WHERE在分组前过滤行且不能用聚合函数,而HAVING可对COUNT、AVG等聚合结果条件过滤,执行顺序为FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY。

用 HAVING 筛选分组后的聚合结果
分组后筛选必须用 HAVING,不是 WHERE。因为 WHERE 在分组前执行,会把整行过滤掉;而 HAVING 作用于 GROUP BY 之后的聚合结果,才能对“每组的 COUNT/SUM/AVG”这类值做判断。
常见错误现象:写成 WHERE COUNT(*) > 1,直接报错——WHERE 不允许用聚合函数。
-
HAVING必须紧跟在GROUP BY后面,不能放在WHERE前或ORDER BY后 - 条件中可直接使用聚合函数,如
HAVING COUNT(*) >= 5、HAVING AVG(score) > 80 - 若同时有
WHERE和HAVING,WHERE先过滤原始行,再分组,最后HAVING过滤组
HAVING 和 WHERE 混用时的执行顺序
理解执行顺序是避免逻辑错误的关键:FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY。
例如要查“每个部门中薪资 > 5000 的员工数 ≥ 3 的部门”,salary > 5000 是行级条件,应放 WHERE 预过滤;而“≥ 3”是组级条件,必须放 HAVING:
SELECT dept_id, COUNT(*) AS high_paid_count FROM employees WHERE salary > 5000 GROUP BY dept_id HAVING COUNT(*) >= 3;
- 如果把
salary > 5000错放到HAVING,语句虽可能通过(如 MySQL 允许),但语义错误:它会先按部门分组、再对每组总人数判断,完全偏离需求 - 如果漏写
WHERE,全表分组后再筛,性能差,尤其大表 -
HAVING中引用的列,要么是GROUP BY列,要么是聚合表达式;不能引用未分组也未聚合的普通列
NULL 值对 HAVING 判断的影响
聚合函数天然忽略 NULL,但 HAVING 条件本身不处理 NULL——它只判断聚合结果是否满足布尔条件。问题常出在聚合源数据含 NULL 时,导致计数或均值失真。
比如统计“每个产品类别的平均折扣率”,若 discount_rate 列大量为 NULL,AVG(discount_rate) 仍会计算非空值的均值,但该均值可能无法代表真实业务分布。
- 用
COUNT(discount_rate)/COUNT(*)可估算该组非空率,辅助判断均值可靠性 - 必要时在
WHERE中排除discount_rate IS NULL,让HAVING基于更干净的数据集 -
HAVING AVG(discount_rate) > 0.1不会因某组全为NULL报错,但会返回NULL,而NULL > 0.1为UNKNOWN,该组被过滤——这是 SQL 三值逻辑的隐性行为,容易被忽略
不同数据库对 HAVING 的兼容性细节
标准 SQL 要求 HAVING 条件中的非聚合列必须出现在 GROUP BY 中,但部分数据库(如 MySQL 5.7 默认模式)允许放宽限制,带来隐患。
例如:SELECT dept_id, MAX(salary) FROM employees HAVING location = 'Beijing' —— location 未分组也未聚合,在 PostgreSQL 或 SQL Server 中直接报错,在宽松模式 MySQL 中可能返回不确定结果。
- 始终显式写出
GROUP BY所有非聚合列,避免跨库迁移失败 - SQL Server 不支持
COUNT(布尔表达式),但HAVING中用COUNT(CASE WHEN ... THEN 1 END) > 0完全合法 - MySQL 8.0+ 和 PostgreSQL 支持窗口函数 +
HAVING组合,但注意窗口函数不能直接用于HAVING,需套子查询
HAVING 时最易被忽略的,是它依赖于前面分组和聚合的完整性——一旦 GROUP BY 漏列、或聚合函数输入被 WHERE 过度削减,HAVING 就成了无本之木。

















