having必须紧跟group by后使用,作用于分组结果而非单行,不能替代where进行行级过滤,且只能引用group by列或聚合函数结果。

HAVING 必须跟在 GROUP BY 后面,不能单独用,也不能放在 WHERE 前面——否则直接报错 SQLSTATE[HY000]: General error: 1140 Mixing of GROUP columns with no GROUP columns is illegal。
HAVING 必须配合 GROUP BY 才能生效
很多人写完 SELECT dept, COUNT(*) FROM emp HAVING COUNT(*) > 5 就报错,原因很简单:没写 GROUP BY dept。数据库根本不知道“按什么分组”,自然没法对“组”做筛选。
-
HAVING的作用对象是“组”,不是“行”,所以必须先有分组动作 - 哪怕只分一组(比如全表聚合),也得显式写
GROUP BY ()(部分数据库支持)或用GROUP BY 1,但更稳妥的是加个常量列:GROUP BY 'all' - MySQL 8.0+ 允许
GROUP BY ()表示无分组聚合,但 PostgreSQL 不支持,跨库时建议避免
WHERE 和 HAVING 混用时,条件放错位置会报错
常见错误是把本该在 WHERE 的条件挪到 HAVING 里,比如:
SELECT dept, AVG(salary) FROM emp GROUP BY dept HAVING status = 'active'; -- ❌ 报错:status 不在 GROUP BY 中,也不是聚合值
正确写法是:
SELECT dept, AVG(salary) FROM emp WHERE status = 'active' -- ✅ 行级过滤,先筛掉非活跃员工 GROUP BY dept HAVING AVG(salary) > 5000; -- ✅ 组级过滤,只留平均工资超标的部门
-
WHERE能用的列,HAVING不一定能用;反之,HAVING能用的聚合函数,WHERE绝对不能用 -
HAVING中可引用SELECT里的别名(如total_sales),但仅限当前查询层级,子查询里不可见 - 性能上,
WHERE过滤越早越好——它减少后续分组的数据量;HAVING是最后一步,处理的是已分好的组
HAVING 中 NULL 值容易导致逻辑意外
当某组所有值都是 NULL,AVG(price) 返回 NULL,而 HAVING AVG(price) > 100 判定结果为 UNKNOWN,该组会被直接排除(MySQL/PostgreSQL 默认行为一致)。
如果你希望保留“有记录但全为 NULL”的组,得显式判断:
HAVING COUNT(*) > 0 AND (AVG(price) IS NOT NULL AND AVG(price) > 100)
- 用
COUNT(col)而非COUNT(*)可区分“有行”和“该列有值” - 跨数据库迁移时,
transform_null_equals = on(PostgreSQL)等配置可能改变NULL = NULL的判定,HAVING条件里尽量避免依赖隐式三值逻辑 - 聚合结果为空时,
HAVING COUNT(*) = 0永远不会命中——因为没行就不会成组,GROUP BY后至少有一行
嵌套聚合必须用子查询,HAVING 无法解决
想查“订单均值高于全站均值的用户”,不能写:
SELECT user_id, AVG(amount) FROM orders GROUP BY user_id HAVING AVG(amount) > AVG(amount); -- ❌ 语法错误:同一层不能两次引用聚合
必须拆成子查询或 CTE:
WITH site_avg AS ( SELECT AVG(amount) AS global_avg FROM orders ) SELECT o.user_id, AVG(o.amount) AS user_avg FROM orders o CROSS JOIN site_avg s GROUP BY o.user_id HAVING AVG(o.amount) > s.global_avg;
-
HAVING只能引用当前分组内的聚合结果,无法跨组或全局引用 - CTE 或子查询虽然多一层,但语义清晰、兼容性好,比强行塞进
HAVING更可靠 - 如果聚合开销大(比如多次
AVG()),子查询还能被优化器复用,而重复写在HAVING里可能被多次计算
真正容易被忽略的不是语法,而是执行顺序带来的列可见性限制——HAVING 看不见 SELECT 里刚起的别名以外的任何新计算列,也看不见 WHERE 里未包含的原始字段。写之前,先默念一遍:from → where → group by → having → select。










