having只能过滤分组后的聚合结果,非聚合字段必须出现在group by中或套用聚合函数;where用于分组前筛选原始数据,having用于分组后筛选count(*)等聚合值。

WHERE 和 HAVING 混用时为什么 HAVING 总报错?
因为 HAVING 只能过滤分组后的结果,不能引用原始表字段(除非该字段在 GROUP BY 中出现或参与聚合)。常见错误是写成 HAVING name = 'Alice',但 name 既没进 GROUP BY,也没套 MAX() 这类聚合函数——这时数据库直接拒绝执行。
-
HAVING的条件表达式里,所有非聚合字段必须出现在GROUP BY子句中 - 想按原始值筛选,先用
WHERE;想按分组统计结果筛选,才用HAVING - MySQL 5.7+ 默认开启
sql_mode=only_full_group_by,会严格校验这点;旧版本可能“放行”,但结果不可靠
怎么用 HAVING 找出订单数超过 5 的客户?
这是最典型场景:先分组,再筛聚合值。关键不是“怎么写”,而是“为什么不能把条件挪到 WHERE”——因为 COUNT(*) 是分组后才有的,WHERE 阶段根本不存在这个值。
- 正确写法:
SELECT customer_id, COUNT(*) AS cnt FROM orders GROUP BY customer_id HAVING COUNT(*) > 5 - 如果加了
WHERE status = 'paid',它会在分组前过滤数据,不影响HAVING逻辑 - 别写
HAVING cnt > 5试图用别名——多数数据库(如 PostgreSQL、SQL Server)不支持在HAVING中用列别名,得重复写COUNT(*)
HAVING 能用子查询或复杂表达式吗?
可以,但要注意执行顺序和性能。数据库先完成分组和聚合,再对每个分组结果执行 HAVING 条件,所以子查询会被反复调用(每组一次),容易拖慢速度。
- 简单表达式安全:
HAVING AVG(price) BETWEEN 100 AND 500 - 带子查询要谨慎:
HAVING COUNT(*) > (SELECT AVG(c) FROM (SELECT COUNT(*) c FROM orders GROUP BY customer_id) t)—— 这种嵌套在分组后算平均订单数,语义清晰但可能全表扫两次 - SQLite 不支持
HAVING中的子查询;MySQL 8.0+ 支持,但需确认执行计划是否走索引
GROUP BY 字段少写了,HAVING 却没报错?
说明你用的是 MySQL 且关闭了 only_full_group_by 模式。此时 MySQL 会“随机”选一个值填充非分组字段,HAVING 看到的可能是任意一行的值,结果不可预测。
- 查当前模式:
SELECT @@sql_mode,确认是否含ONLY_FULL_GROUP_BY - 修复方式:要么补全
GROUP BY字段,要么把字段包进聚合函数,比如MAX(name) - PostgreSQL 和 SQL Server 会直接报错,不给你蒙混过关的机会
真正麻烦的不是语法写不对,而是 HAVING 看似跑通了,结果却随数据顺序变化——尤其上线后数据量变大,这种隐性问题才暴露出来。










