首页/数据/数据库速查/HAVING 分组后过滤
SQL 语法52 条

HAVING 分组后过滤

对聚合结果过滤只能用 HAVING;能提前到 WHERE 的条件尽量下推,HAVING 只留聚合判断。

数据库速查数据更新于 2026-10-13

HAVING 是什么

HAVING 用于对分组之后的结果做过滤,也就是「过滤组」而不是「过滤行」,执行顺序位于 GROUP BY 之后、ORDER BY 之前。能提前到 WHERE 的条件一定要下推,HAVING 里只保留必须依赖聚合结果的判断,否则会先完成大量分组再把结果丢掉,代价明显更高。

基本信息

用途分组结果过滤
执行位置GROUP BY 之后、ORDER BY 之前
与 WHERE 的区别WHERE 过滤行,HAVING 过滤组
别名引用MySQL 支持引用 SELECT 别名,标准 SQL 更严格
性能要点普通条件尽量写进 WHERE

典型使用场景

  • 筛选下单次数超过阈值的用户
  • 找出累计金额达到门槛的客户
  • 配合 COUNT、SUM、AVG 做分组后的结果收敛
  • 多维度统计后再过滤,例如按渠道加地区分组
  • 排查「把条件写在 HAVING 里导致查询变慢」

用法示例

用法示例
SELECT user_id, COUNT(*) AS cnt, SUM(amount) AS total
FROM orders
WHERE created_at >= '2026-09-01'   -- 先筛行:能下推的都放这里
GROUP BY user_id
HAVING cnt >= 5 AND total > 1000   -- 再筛组
ORDER BY total DESC
LIMIT 20;

-- 可移植写法:不引用别名,直接写聚合表达式
-- HAVING COUNT(*) >= 5 AND SUM(amount) > 1000

WHERE 与 HAVING 对照

子句作用时机注意事项
WHERE分组前过滤行能用索引,代价最低
HAVING分组后过滤组只能引用分组列与聚合函数
别名引用HAVING 中引用 SELECT 别名MySQL 支持,跨库不保证
聚合条件COUNT / SUM / AVG 等无法下推到 WHERE
组合使用先 WHERE 再 GROUP BY 再 HAVING顺序不能颠倒
把普通条件写进 HAVING 会让引擎先完成全部分组再丢弃结果:执行代价随分组数量增长,而写在 WHERE 里可以利用索引提前缩小数据量。改写时先自问一句:这个条件能不能在分组之前就判断完。
MySQL 允许 HAVING 引用 SELECT 别名,这让 SQL 更易读;但跨数据库或严格模式下可能不兼容。对可移植性要求高的公共脚本,建议直接写聚合表达式本身,读起来长一点但更省心。
HAVING 中引用未分组的普通列存在语义歧义:开启 ONLY_FULL_GROUP_BY 后会直接报错,这通常说明 SQL 本身写得不够明确,应该补全分组列。

易错点

  • 把本可在 WHERE 的普通条件写进 HAVING:先分组再过滤,性能明显更差
  • HAVING 引用别名后在其它数据库执行失败:可移植性被牺牲
  • 以为 HAVING 能用上索引:它作用在聚合结果上,基本不走索引
  • 漏写 GROUP BY 却写 HAVING:语义含糊,在严格模式下直接报错
  • 把聚合条件写进 WHERE:聚合函数不能用于 WHERE,语句直接报错

数据来源与参考

  • MySQL 8.0 Reference Manual: SELECT - HAVING Clause

相关工具

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌