HAVING必须紧跟GROUP BY后使用,作用于分组后的结果集,不能单独使用或引用未分组的原始列;执行顺序为FROM→WHERE→GROUP BY→HAVING,跳过GROUP BY会导致报错。

HAVING 必须跟在 GROUP BY 后面,否则直接报错
单独写 HAVING COUNT(*) > 1 会触发 ERROR 1140:In aggregated query without GROUP BY。这不是数据库“不支持”,而是 SQL 标准要求——HAVING 的作用对象是分组后的结果集,没分组就没有“组”,自然没法算 COUNT 或 AVG。
执行顺序固定为:FROM → WHERE → GROUP BY → HAVING。跳过 GROUP BY,HAVING 就失去上下文。
- MySQL 8.0+、PostgreSQL、SQL Server 全部拒绝无
GROUP BY的HAVING - 旧版 MySQL(5.7 且关闭
ONLY_FULL_GROUP_BY)可能允许,但结果不可靠,上线后极易翻车 - 想对全表聚合结果做判断(比如“总订单数是否超 100”),用子查询更清晰:
SELECT * FROM (SELECT COUNT(*) AS cnt FROM orders) t WHERE cnt > 100
WHERE 和 HAVING 别混用:一个筛行,一个筛组
WHERE status = 'paid' 是合法的,HAVING status = 'paid' 几乎总是错的——因为 status 是单行字段,不是分组键,也没被聚合,数据库不知道该取哪一行的值。
典型误用:
- 把本该前置的条件塞进
HAVING:HAVING status = 'paid' AND COUNT(*) >= 3→ 先对所有状态分组,再丢弃非 paid 组,白白浪费计算资源 - 正确做法:
WHERE status = 'paid'放前面,HAVING COUNT(*) >= 3留后面 -
WHERE不能用COUNT(*),HAVING不能用未分组未聚合的原始列(如user_name)
GROUP BY 字段必须包含 SELECT 中所有非聚合列
写 SELECT u.id, u.name, COUNT(o.id) FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.id HAVING COUNT(o.id) >= 3,MySQL 8.0+ 直接报错:Expression #2 of SELECT list is not in GROUP BY clause。
原因:u.name 没参与分组,数据库无法确定“这一组里多个 name 取哪一个”。这不是 bug,是 SQL 标准强制行为。
- 要么删掉
u.name,只选u.id和聚合值 - 要么补全分组:
GROUP BY u.id, u.name - 别依赖 MySQL 5.7 的宽松模式——PostgreSQL、Oracle、SQL Server 全部严格校验,跨库迁移时必崩
HAVING 里不能用 SELECT 别名,聚合表达式要写全
写 SELECT user_id, COUNT(*) AS cnt FROM orders GROUP BY user_id HAVING cnt > 5,在 PostgreSQL 和 SQL Server 里会报错;MySQL 虽支持,但属于特例,不可移植。
HAVING 看不到 SELECT 列别名,它只认三类东西:分组字段、聚合函数、由它们组成的表达式。
- 安全写法:
HAVING COUNT(*) > 5或HAVING COUNT(o.id) > 5 - 需要多条件时,别硬塞一堆
AND:HAVING COUNT(*) >= 3 AND AVG(amount) > 1000可以,但复杂逻辑建议用SUM(CASE WHEN ... THEN 1 ELSE 0 END)构造标量标记 -
CASE WHEN必须带ELSE 0,否则遇到NULL行,SUM结果变NULL,整个条件判定为 unknown,记录被静默过滤
真正容易被忽略的是:HAVING 不加速,也不走索引。它只是对 GROUP BY 产出的临时结果集做内存过滤。性能瓶颈永远在 GROUP BY 本身——数据量一大,先用 WHERE 把无关行干掉,比在 HAVING 里加一堆条件有用得多。

















