WHERE在GROUP BY前过滤行,不能用聚合函数;HAVING在GROUP BY后过滤分组结果,专用于聚合条件或分组字段,二者分工明确、不可互换。

WHERE 不能用在 GROUP BY 后面,因为执行顺序不对
很多人写 SELECT region, COUNT(*) FROM sales WHERE COUNT(*) > 10 GROUP BY region,结果报错 ERROR: aggregate functions are not allowed in WHERE。这不是语法不熟,是没理解 SQL 执行顺序:WHERE 在 GROUP BY 和聚合计算之前就执行了,这时候 COUNT(*) 还不存在,自然不能参与过滤。
真正该用的位置是 HAVING —— 它专为过滤分组后的结果而设计,执行时机在 GROUP BY 和所有聚合函数完成之后。
HAVING 必须跟在 GROUP BY 后面,且只能写聚合条件或分组字段
HAVING 不是 WHERE 的替代品,而是分工明确的搭档。它只接受两类表达式:
- 聚合函数,比如
COUNT(*)、SUM(amount)、AVG(price) - 出现在
GROUP BY子句中的字段,比如GROUP BY region→ 可以写HAVING region != 'Unknown'
下面这个写法是合法的:
SELECT region, COUNT(*) AS cnt FROM sales GROUP BY region HAVING COUNT(*) > 5 AND AVG(amount) >= 1000;
但下面这个会报错:HAVING amount > 1000(amount 没分组也没聚合,上下文里不存在)。
WHERE 和 HAVING 联合使用时,注意逻辑边界
常见误区是把所有过滤都塞进 HAVING,导致性能下降。其实应该按阶段拆分:
-
WHERE过滤原始行(如时间范围、状态标记),减少参与分组的数据量 -
HAVING过滤分组结果(如“订单数超 10 的地区”“平均客单价低于 200 的品类”)
例如统计 2024 年活跃客户中,人均订单数 > 3 的城市:
SELECT city, COUNT(*) / COUNT(DISTINCT customer_id) AS avg_orders_per_customer FROM orders WHERE order_date >= '2024-01-01' -- 先筛年份,别让旧数据进分组 GROUP BY city HAVING COUNT(*) > 3 * COUNT(DISTINCT customer_id); -- 再筛分组后指标
如果把 WHERE order_date >= ... 挪到 HAVING 里,语义错误且数据库根本不会执行。
MySQL 5.7+ 默认开启 ONLY_FULL_GROUP_BY,HAVING 里混用非分组字段会报错
在较新 MySQL 版本中,执行 SELECT region, COUNT(*) FROM sales GROUP BY region HAVING amount > 100 会直接失败,提示 Expression #1 of SELECT list is not in GROUP BY clause。这不是 HAVING 本身的问题,而是 SQL 模式限制了“非确定性输出”。
解决方法只有两个:
- 确保
HAVING中所有非聚合字段都出现在GROUP BY中(推荐) - 临时关闭
ONLY_FULL_GROUP_BY(不推荐,掩盖逻辑缺陷)
真正的复杂点不在语法,而在于想清楚:你过滤的到底是“哪一组数据”,以及“依据哪个聚合结果”。漏掉这一层,HAVING 就只是个报错触发器。










