HAVING必须紧随GROUP BY之后且不可省略,即使全局聚合也需显式写GROUP BY();仅能引用分组字段、聚合函数或SELECT别名;WHERE应过滤原始行,HAVING筛选分组结果,跨组比较等复杂逻辑需用子查询或窗口函数。

HAVING 必须跟在 GROUP BY 后面,且不能省略 GROUP BY
没写 GROUP BY,HAVING 就没有“组”可筛。哪怕你只想要一个全局聚合值(比如 COUNT(*) > 1000),也得显式写 GROUP BY ()。MySQL 允许隐式单一分组,但 PostgreSQL 会直接报错:column "xxx" must appear in the GROUP BY clause。跨库迁移时漏掉 GROUP BY () 是高频翻车点。
-
GROUP BY是前提,HAVING是后续动作,二者必须成对出现 - 想查全表总记录数是否超限?写
SELECT COUNT(*) FROM t HAVING COUNT(*) > 1000在 PostgreSQL 里会失败,必须补上GROUP BY () - MySQL 5.7+ 默认开启
ONLY_FULL_GROUP_BY模式,此时连SELECT * FROM t GROUP BY id HAVING COUNT(*) > 1都可能报错——因为*展开的字段未全部出现在GROUP BY中
HAVING 只能引用三类表达式:分组字段、聚合函数、SELECT 别名
HAVING 的作用对象是“组”,不是原始行。它能安全使用的只有:已出现在 GROUP BY 中的字段(如 dept)、任意聚合函数(如 COUNT(*)、AVG(salary))、或 SELECT 中明确定义的别名(如 avg_sal)。其他一律不行。
- 允许:
HAVING AVG(salary) > 15000、HAVING dept = 'HR'、SELECT dept, AVG(salary) AS avg_sal ... HAVING avg_sal > 15000 - 禁止:
HAVING status = 'active'(status没进GROUP BY,也没被聚合) - 注意兼容性:
SELECT AVG(score) AS avg_score FROM t GROUP BY class HAVING avg_score > 4.5在 SQLite 和旧版 MySQL 中不被支持,得写成HAVING AVG(score) > 4.5
WHERE 和 HAVING 必须分工明确,混用会导致性能暴跌或报错
WHERE 筛的是原始行,HAVING 筛的是分组后结果。把本该放 WHERE 的条件(如时间范围、状态标记)塞进 HAVING,数据库会先对全表分组(可能生成上千个空组),再逐个判断丢弃,白白消耗 CPU 和内存。
- 正确:
WHERE order_date >= '2024-01-01' GROUP BY customer_id HAVING COUNT(*) > 5 - 错误:
GROUP BY customer_id HAVING order_date >= '2024-01-01' AND COUNT(*) > 5→ 报错(order_date未分组)或逻辑错(先分所有年份的订单再筛) - 更隐蔽的坑:
HAVING COUNT(*) * 1.2 > 100看似合法,但如果COUNT(*)因浮点误差算出83.333...,乘完可能刚好卡在边界上;建议统一用整型比较或显式ROUND
复杂二次筛选别硬扛,该上子查询或窗口函数就上
HAVING 只能做“组内判断”。一旦涉及跨组比较(比如“高于部门平均值”)、否定逻辑(比如“买过 A 但没买过 B”)、或多阶段聚合(比如“连续 3 个月达标”),它就无能为力。
- 需要全局均值对比?用标量子查询:
HAVING SUM(amount) > (SELECT AVG(total) FROM (SELECT SUM(amount) AS total FROM sales GROUP BY dept) t) - 要取每组销量前三?
HAVING不支持排名,改用ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales DESC) - 子查询别名不能省:
SELECT * FROM (SELECT dept, COUNT(*) c FROM t GROUP BY dept) tmp WHERE c > 10在 MySQL/PostgreSQL 中必须加AS tmp,否则报Every derived table must have its own alias
真正容易被忽略的,是 HAVING 后面的 ORDER BY —— 它排的是聚合后的结果集,不是原始行;而且只要 SELECT 里定义了别名,ORDER BY 就可以直接用,不用重复写整个聚合表达式。

















