WHERE用于分组前过滤行,HAVING用于GROUP BY后筛选分组结果;COUNT(*)等聚合值只能在HAVING中使用,WHERE中不可用,且HAVING必须紧跟GROUP BY。

WHERE 不能用在分组后筛选,必须用 HAVING
WHERE 是在分组前过滤行,HAVING 才是在 GROUP BY 之后对分组结果做条件筛选。想筛“每组记录数大于 N”,WHERE 根本看不到 COUNT(*) 的值,硬写会报错:ERROR: column "count" does not exist 或类似提示。
常见错误现象:把 HAVING count > 5 写成 WHERE count > 5,或者漏写 GROUP BY 就直接用 HAVING,导致 SQL 报语法错误。
-
HAVING必须跟在GROUP BY后面(或隐含分组,如全表聚合) -
HAVING中能用的字段,只能是GROUP BY列或聚合函数(如COUNT(*)、SUM(price)) - 如果只想要全局满足条件的分组数,别在
SELECT里多选无关列,避免语义模糊
HAVING 和 COUNT(*) 配合的典型写法
最常用场景就是统计每个分组数量并设下限。比如查「订单数超过 3 的用户」:
SELECT user_id, COUNT(*) AS order_cnt FROM orders GROUP BY user_id HAVING COUNT(*) > 3;
注意点:
-
COUNT(*)和COUNT(column)行为不同:后者忽略NULL,前者不忽略;多数情况用COUNT(*)更安全 - 别写
HAVING order_cnt > 3—— 别名在HAVING中不可用(除非数据库明确支持,如 PostgreSQL 允许,但 MySQL 不行),得重复写聚合表达式 - 如果加了
WHERE,它先执行:比如先筛WHERE status = 'paid',再分组计数,最后HAVING筛分组结果
性能和兼容性要注意的地方
HAVING 本身不直接拖慢查询,但它依赖分组结果集大小。如果 GROUP BY 前没加有效 WHERE 过滤,可能产生大量中间分组,内存和 CPU 消耗陡增。
- MySQL 5.7+ 默认开启
sql_mode=only_full_group_by,要求SELECT中所有非聚合字段必须出现在GROUP BY中,否则报错;HAVING不影响该模式判断 - PostgreSQL 对
HAVING更宽松,允许部分别名引用,但跨版本行为不一致,建议统一用原始表达式 - SQLite 支持
HAVING,但不支持窗口函数配合HAVING,别混用
容易被当成 WHERE 用的 HAVING 误用场景
比如想查「2024 年下单且订单数 > 2 的用户」,错误写法是:
SELECT user_id, COUNT(*) FROM orders GROUP BY user_id HAVING created_at >= '2024-01-01' AND COUNT(*) > 2;
这会失败,因为 created_at 不在 GROUP BY 中,也不是聚合字段。正确做法是把时间条件放进 WHERE:
SELECT user_id, COUNT(*) FROM orders WHERE created_at >= '2024-01-01' GROUP BY user_id HAVING COUNT(*) > 2;
关键区别:
-
WHERE过滤原始行(单条记录) -
HAVING过滤分组后的“一行代表一组”的结果 - 混用时顺序固定:FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
真正难的不是写对 HAVING,而是想清楚你要筛的是“哪些原始数据”还是“哪些分组结果”。一旦搞反,查出来的数据就不是你想要的。

















