having必须跟在group by后用于分组后过滤,不能替代where;where在分组前过滤且可走索引,having操作内存中分组结果;having只能引用group by列或聚合表达式,不可用select别名(除mysql宽松支持),复杂逻辑应优先用子查询实现。

HAVING 必须跟在 GROUP BY 后面,不能替代 WHERE
WHERE 在分组前过滤行,HAVING 在分组后过滤组。如果你把条件写在 WHERE 里却想用聚合函数(比如 COUNT()、AVG()),数据库会直接报错:ERROR: aggregate functions are not allowed in WHERE。这是最常见的误用起点。
正确顺序只能是:SELECT ... FROM ... GROUP BY ... HAVING ...。没有 GROUP BY,HAVING 就无效(某些数据库如 MySQL 允许无 GROUP BY 的 HAVING,但语义模糊,应避免)。
- 想筛“订单数超过 5 的客户”?用
HAVING COUNT(*) > 5,不是WHERE COUNT(*) > 5 - 想筛“平均单价高于 100 的品类”,必须先
GROUP BY category,再HAVING AVG(price) > 100 - WHERE 可以用索引加速,HAVING 无法利用索引——它操作的是内存中的分组结果,大数据量时性能更敏感
HAVING 中能用的字段,仅限 SELECT 和 GROUP BY 中出现的列或聚合表达式
数据库不允许你在 HAVING 里引用一个既没出现在 GROUP BY 里、也没被聚合函数包裹的列。例如:SELECT department, AVG(salary) FROM emp GROUP BY department HAVING name = 'Alice' —— 这会报错:column "name" must appear in the GROUP BY clause or be used in an aggregate function。
- 合法写法:用
HAVING AVG(salary) > 8000或HAVING MAX(hire_date) > '2020-01-01' - 如果真要结合非分组字段逻辑,得先在子查询或 CTE 中算出该字段的聚合值,再外层 HAVING
- 注意 NULL:
HAVING COUNT(status) > 0和HAVING COUNT(*) > 0行为不同——前者忽略status为 NULL 的行,后者统计所有行
MySQL 和 PostgreSQL 对 HAVING 的宽松程度不同,别依赖隐式行为
MySQL 允许 HAVING 引用 SELECT 列别名(如 SELECT AVG(x) AS avg_x FROM t GROUP BY y HAVING avg_x > 10),PostgreSQL 不允许,会报 column "avg_x" does not exist。SQL 标准规定:HAVING 不能引用别名,只认原始列或聚合表达式。
- 安全写法:重复表达式,如
HAVING AVG(x) > 10,而不是HAVING avg_x > 10 - 别名可用在 ORDER BY 中,但不可用于 HAVING —— 这个边界容易混淆
- 某些旧版 MySQL(5.7 之前)甚至允许 HAVING 不带 GROUP BY,返回单行结果,这种行为在 8.0+ 默认关闭(
sql_mode含ONLY_FULL_GROUP_BY时强制校验)
复杂过滤建议拆到子查询,别硬塞进 HAVING
当 HAVING 条件开始嵌套函数、多层逻辑(比如 “过去 3 个月下单次数 ≥2 且平均间隔 ≤7 天”),可读性和维护性会断崖下跌。此时不如把聚合逻辑封装进子查询,外层用 WHERE 做最终筛选。
例如:
SELECT department, avg_salary
FROM (
SELECT department, AVG(salary) AS avg_salary,
COUNT(*) AS emp_count
FROM employees
GROUP BY department
) t
WHERE avg_salary > 10000 AND emp_count >= 5;
- 子查询中完成分组和基础聚合,外层 WHERE 更易调试、加索引、复用
- HAVING 适合单一、明确的聚合阈值判断;一旦涉及多个聚合指标交叉比较,优先考虑子查询结构
- 某些数据库(如 SQL Server)对 HAVING 子句长度或嵌套层级有限制,超限时会静默截断或报错










