HAVING用于GROUP BY后筛选分组,必须依赖分组结果,可直接使用聚合函数或SELECT别名,但不可引用未分组/未聚合的原始字段;WHERE筛行、HAVING筛组,结构为FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY。

要用 HAVING 对聚合后的分组数据做二次过滤,关键是把它放在 GROUP BY 之后、SELECT 产出结果之前,专用于判断每组的聚合值是否达标。
必须先分组,再用 HAVING 筛组
HAVING 不是独立存在的子句,它只对 GROUP BY 产生的结果起作用。没写 GROUP BY,HAVING 就没有“组”可筛——除非整条语句本身只产生一个聚合结果(比如 SELECT COUNT(*)),此时数据库会隐式当作单一分组处理,但这种写法跨库兼容性差,建议显式加上 GROUP BY ()。
- 正确结构:FROM → WHERE(可选)→ GROUP BY → HAVING → SELECT → ORDER BY
- HAVING 可直接使用 COUNT()、SUM()、AVG() 等聚合函数,也能用 SELECT 中定义的别名(如 avg_sal)
- 不能在 HAVING 里引用原始表中的普通字段(如 user_name),除非它已出现在 GROUP BY 中或被聚合函数包裹
WHERE 和 HAVING 别混用
WHERE 是在分组前筛行,HAVING 是在分组后筛组。把本该放 WHERE 的条件(比如 status = 'active' 或 order_date > '2024-01-01')错塞进 HAVING,会导致全表先分组再丢弃,白白消耗资源,还可能报错。
- 想只统计“已激活用户”的部门平均薪资?WHERE status = 'active' 再 GROUP BY dept
- 想保留“平均薪资超 15000 的部门”?HAVING AVG(salary) > 15000
- 错误示例:HAVING status = 'active'(status 没参与分组也没聚合)→ 多数数据库直接拒绝
多个条件组合时注意逻辑和精度
HAVING 支持 AND、OR 和括号,适合表达复合业务规则,但要注意聚合表达式必须与 SELECT 中一致,尤其涉及四舍五入、类型转换或 NULL 时。
- 查“总销售额 > 10 万且订单数 100000 AND COUNT(*)
- 查“今年和去年销售额都超 10 万的地区”:用 CASE 配合 SUM 实现年度拆解
- 若 SELECT 中写了 ROUND(AVG(score), 2) AS avg_score,则 HAVING 也得用 ROUND(AVG(score), 2) > 4.5,否则可能因浮点误差漏判
性能和可靠性要点
HAVING 本身不走索引,所有判断都在内存中完成。所以前期尽量用 WHERE 减少输入行数;同时开启 ONLY_FULL_GROUP_BY 模式,能提前暴露非聚合字段误用问题,避免返回不可靠的随机值。
- 先 WHERE 筛时间范围、状态、类别,再 GROUP BY,最后 HAVING 控制聚合阈值
- 复杂逻辑(如“连续 3 个月达标”)超出 HAVING 能力,应改用窗口函数或 CTE 分步处理
- 含子查询的 HAVING(如 HAVING AVG(x) > (SELECT AVG(y) FROM …))慎用——多数引擎会对每组重复执行子查询,应提至 FROM 作为派生表

















