mysql中HAVING子句与WHERE子句的差异与应用

9次阅读

WHERE过滤行、HAVING过滤分组;WHERE在聚合前执行且不可用聚合函数,HAVING在GROUP BY后执行且必须依赖聚合结果;二者可共存,顺序为WHERE→GROUP BY→HAVING。

mysql中HAVING子句与WHERE子句的差异与应用

WHERE 是过滤行,HAVING 是过滤分组

WHERE 在聚合计算前就筛掉不满足条件的原始行,HAVING 则是在 GROUP BY 完成、聚合函数(如 count()SUM())算出结果后,对分组结果再筛选。这意味着:WHERE 不能用聚合函数,而 HAVING 必须依赖分组或聚合结果。

WHERE 无法写 COUNT(*) > 5,但 HAVING 可以

常见错误是把本该放 HAVING 的条件误写进 WHERE,比如想查“订单数超过 5 的用户”,却写成:

SELECT user_id, COUNT(*) FROM orders WHERE COUNT(*) > 5 GROUP BY user_id;

这会直接报错 Invalid use of group function。正确写法是:

SELECT user_id, COUNT(*) AS cnt FROM orders GROUP BY user_id HAVING cnt > 5;
  • HAVING 后可直接用别名(如 cnt)或原聚合表达式(如 COUNT(*) > 5
  • WHERE 只能作用于原始字段,例如 WHERE status = 'paid',且必须写在 GROUP BY
  • 若同时需要行级过滤和分组后过滤,WHEREHAVING 可共存,顺序固定为:WHERE → GROUP BY → HAVING

性能差异:WHERE 通常比 HAVING 更快

因为 WHERE 减少了参与分组的行数,数据库不用为被过滤掉的行做分组和聚合计算。而 HAVING 是在所有分组完成后再过滤,可能白白算了很多组。

  • 优先把能下推到 WHERE 的条件放过去,比如时间范围、状态码、非空校验
  • 如果某条件依赖聚合结果(如“平均单价 > 100”),只能放 HAVING,没有替代方案
  • 在大表上,HAVING COUNT(*) > 1WHERE 多扫一遍数据,但有时配合索引(如联合索引覆盖 GROUP BY 字段)也能接受

没写 GROUP BY 时,HAVING 还能用吗?

可以,但语义变了:整个查询结果被视为一个分组。此时 HAVING 相当于带聚合的全局过滤器。

SELECT COUNT(*) AS total FROM users HAVING total > 1000;

这条语句不会返回任何行,除非总记录数真大于 1000。它等价于:

SELECT COUNT(*) AS total FROM users WHERE (SELECT COUNT(*) FROM users) > 1000;

虽然语法合法,但实际中极少这么用——更清晰的做法是用 select + 应用层判断,或用子查询加 WHERE。滥用无 GROUP BYHAVING 容易让人误解执行逻辑。

真正容易被忽略的是:HAVING 的执行时机紧贴 GROUP BY 输出,哪怕只有一行结果,它也等聚合完成才介入。这个“等待聚合”的特性,决定了它永远无法替代 WHERE 的前置剪枝能力。

text=ZqhQzanResources