having必须跟在group by后使用,用于筛选分组后的聚合结果;若省略group by直接写having,mysql会报error 1140,因其作用对象是分组生成的组级行,而非原始行。

HAVING 必须跟在 GROUP BY 后面,不能单独用
直接写 HAVING 而不带 GROUP BY 会报错,比如 MySQL 报 ERROR 1140: In aggregated query without GROUP BY。因为 HAVING 的作用对象是分组后的行(即每个分组聚合出的一行),不是原始数据行。
常见错误是想“先聚合再筛选”,却漏掉分组——比如想查“订单总金额超过 1000 的客户”,却只写 SELECT customer_id, SUM(amount) FROM orders HAVING SUM(amount) > 1000,这语法非法。
- 必须写成:
SELECT customer_id, SUM(amount) FROM orders GROUP BY customer_id HAVING SUM(amount) > 1000 -
HAVING中出现的列,要么是GROUP BY列,要么是聚合函数(如SUM()、COUNT()) - 不能在
HAVING中引用未聚合也未分组的列,例如HAVING order_date > '2023-01-01'(除非order_date在GROUP BY中)
WHERE 和 HAVING 的分工必须分清
WHERE 过滤的是“分组前”的原始行,HAVING 过滤的是“分组后”的聚合行。混淆两者会导致逻辑错误或性能浪费。
比如要查“2023 年下单次数 ≥ 5 次且平均订单金额 > 300 的客户”:
- 年份筛选(
order_date >= '2023-01-01')应放WHERE:提前减少扫描行数 - 次数和平均值是分组结果,必须放
HAVING:HAVING COUNT(*) >= 5 AND AVG(amount) > 300 - 如果把
COUNT(*) >= 5错放到WHERE,会直接报错 —— 因为WHERE不允许用聚合函数
MySQL 8.0+ 支持 HAVING 引用 SELECT 别名,但其他数据库不一定
MySQL 8.0 开始允许在 HAVING 中直接使用 SELECT 子句定义的别名,比如:
SELECT customer_id, SUM(amount) AS total FROM orders GROUP BY customer_id HAVING total > 1000
但 PostgreSQL、SQL Server、SQLite 等多数数据库不支持这种写法,会报错 column "total" does not exist。
- 兼容写法:重复表达式,写成
HAVING SUM(amount) > 1000 - 或者用子查询 / CTE 绕过,但增加嵌套层级
- 别名在
HAVING中可用 ≠ 在WHERE中可用:所有 SQL 都不允许WHERE引用SELECT别名
NULL 值在 HAVING 条件中容易被忽略
聚合函数对 NULL 的处理会影响 HAVING 结果。比如 COUNT(*) 统计所有行,而 COUNT(column) 自动跳过该列值为 NULL 的行。
典型陷阱:用 HAVING COUNT(status) = 0 想找“没有任何有效状态记录的用户”,结果为空——因为如果某用户所有 status 都是 NULL,COUNT(status) 确实为 0;但如果该用户根本没记录,GROUP BY 就不会生成对应分组行,自然不会进入 HAVING 判断。
- 确认业务语义:是“有记录但 status 全 NULL”,还是“压根没记录”?后者需用
LEFT JOIN+IS NULL -
SUM()、AVG()遇到全 NULL 输入会返回NULL,而NULL > 100结果为UNKNOWN,该分组会被过滤掉(三值逻辑) - 必要时用
COALESCE(SUM(amount), 0) > 100显式转默认值
HAVING 就会静默失效或返回意外结果。











