HAVING必须跟在GROUP BY后使用,因它仅作用于分组后的聚合结果;单独使用会报错(如MySQL的ERROR 1140),因其过滤对象是“组级行”,而非原始行。

必须先写 GROUP BY,再写 HAVING;单独用 HAVING 会直接报错,因为数据库根本没“组”可筛。
为什么 HAVING 不能单独出现
HAVING 的作用对象是分组后产生的“组级行”,不是原始数据行。没有 GROUP BY,就不存在分组结果,HAVING 条件无处依附。
- MySQL 报错:
ERROR 1140: In aggregated query without GROUP BY - PostgreSQL 报错:
HAVING clause requires GROUP BY - SQLite 报错:
misuse of aggregate
常见错误写法:SELECT customer_id, SUM(amount) FROM orders HAVING SUM(amount) > 1000 —— 缺少 GROUP BY customer_id,语法非法。
HAVING 能用什么,不能用什么
HAVING 只能引用两类东西:已出现在 GROUP BY 中的字段(或其表达式),或聚合函数(如 COUNT(*)、AVG(salary))。
- ✅ 合法:
HAVING department = 'tech'(department在GROUP BY中) - ✅ 合法:
HAVING COUNT(*) > 5、HAVING AVG(price) >= 100 - ❌ 非法:
HAVING salary > 5000(salary既没分组也没聚合) - ❌ 非法:
HAVING order_date > '2023-01-01'(除非它也在GROUP BY里)
注意:MySQL 8.0+ 允许在 HAVING 中用 SELECT 别名(如 HAVING total > 1000),但 PostgreSQL、SQL Server 等不支持,跨库迁移时务必重写为原始表达式。
WHERE 和 HAVING 混用时,顺序和分工不能乱
执行顺序固定为:FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY。这个顺序决定了什么该放哪、为什么放那里。
-
WHERE是“删行”:提前过滤掉无关原始记录,大幅减少后续分组计算量(比如WHERE status = 'active') -
HAVING是“删组”:等所有组的聚合值都算完了,才一个个判断是否保留(比如HAVING AVG(salary) > 12000) - 把本该放
WHERE的条件硬塞进HAVING,不仅可能报错,还会让数据库白算一堆不该算的组
典型场景示例:查“2025年下单≥3次且平均订单额>300的客户”——WHERE order_date >= '2025-01-01' 必须前置,HAVING COUNT(*) >= 3 AND AVG(amount) > 300 才是正确后置逻辑。
NULL 值和性能容易被忽略
聚合结果含 NULL 时,HAVING 条件行为可能不符合直觉:
-
HAVING AVG(x) > 100:若某组x全为NULL,AVG(x)返回NULL,而NULL > 100判为UNKNOWN,该组被丢弃 - 稳妥写法:
HAVING AVG(x) IS NOT NULL AND AVG(x) > 100 - 性能上,
HAVING无法跳过聚合计算,所以别指望它“优化速度”——真正提速靠的是WHERE过滤和GROUP BY字段上的索引
最常被漏掉的其实是执行顺序的刚性:HAVING 看不见 WHERE 之后、GROUP BY 之前的数据形态,也看不见 SELECT 里定义的别名(除非 MySQL 8.0+ 且你只跑 MySQL)。它只认分组键和聚合结果这两样东西。

















