HAVING用于分组后筛选聚合结果,必须跟在GROUP BY后,可使用COUNT()等聚合函数;WHERE在分组前过滤行,不可用聚合函数,二者执行顺序为WHERE→GROUP BY→HAVING。

用 HAVING 过滤分组后的数量
WHERE 不能用聚合函数,所以统计完再筛选必须用 HAVING。它在 GROUP BY 之后执行,能直接对 COUNT()、SUM() 等结果做判断。
常见错误是把条件写在 WHERE 里,比如 WHERE COUNT(*) > 5 —— 这会报错:ERROR: aggregate functions are not allowed in WHERE。
-
HAVING必须跟在GROUP BY后面,没分组时不能用 - 如果同时有
WHERE和HAVING,WHERE先过滤行,HAVING再过滤组 - 想让
HAVING生效,SELECT 列表里最好包含被分组的字段(否则语义模糊)
统计用户订单数超过 3 的买家
典型场景:查每个买家下了多少单,只保留下单超阈值的人。注意别漏掉 GROUP BY user_id,否则 COUNT(*) 会算全表总数,HAVING 就失去意义。
SELECT user_id, COUNT(*) AS order_count FROM orders GROUP BY user_id HAVING COUNT(*) > 3;
如果还想看买家姓名,得确保 name 在 GROUP BY 中或用聚合函数包裹(如 MAX(name)),否则 PostgreSQL 或 MySQL 8.0+ 会报错:column "name" must appear in the GROUP BY clause。
WHERE 和 HAVING 混用时的执行顺序
先 WHERE 排除无效记录(比如只看已支付订单),再分组,最后 HAVING 筛组。顺序错了结果就偏了。
- 错误写法:
HAVING status = 'paid'——status是行级字段,不能在分组后直接用 - 正确写法:把状态过滤放
WHERE status = 'paid',再GROUP BY,最后HAVING COUNT(*) > 5 - 性能影响:
WHERE越早缩小数据集,分组越快;HAVING是对内存中分组结果扫描,数据量大时明显变慢
MySQL 与 PostgreSQL 对 NULL 的处理差异
如果分组字段含 NULL,不同数据库行为不一致:MySQL 把所有 NULL 归为同一组;PostgreSQL 也如此,但某些旧版本或特定配置下可能跳过 NULL 组 —— 导致 HAVING 没命中。
- 稳妥做法:显式排除
WHERE group_col IS NOT NULL,避免依赖默认行为 - 想单独统计
NULL组?加GROUP BY group_col后,HAVING group_col IS NULL可行,但需确认目标字段允许为NULL - 别用
COUNT(group_col)替代COUNT(*)来“忽略 NULL”——它本就不计NULL,和HAVING逻辑无关
分组统计的边界情况多在 NULL、空字符串、隐式类型转换上,实际跑之前最好用 SELECT group_col, COUNT(*) 先看原始分组结果。

















