having必须紧跟group by之后,用于过滤分组后的聚合结果;单独使用会报错,因它作用于分组产生的每组聚合值而非原始行,且条件中只能用聚合函数或group by字段。

HAVING 必须跟在 GROUP BY 后面,且只能用于过滤分组后的聚合结果;把 WHERE 条件错放 HAVING 里,会先全表分组再丢弃,性能暴跌甚至报错。
HAVING 必须配合 GROUP BY 使用
单独写 HAVING COUNT(*) > 1 是无效的(除极少数数据库对单行聚合做隐式分组,但 MySQL、PostgreSQL、SQL Server 都不保证兼容)。真正起作用的前提是已有 GROUP BY 产生“组”。比如统计每个部门人数并筛掉人数 ≤ 3 的部门,必须写成:
SELECT department, COUNT(*) AS cnt FROM employees GROUP BY department HAVING COUNT(*) > 3;
如果漏掉 GROUP BY department,MySQL 会报错 ERROR 1140: In aggregated query without GROUP BY,PostgreSQL 直接拒绝执行。
WHERE 和 HAVING 混用时的分工边界
常见错误是把本该在分组前过滤的条件塞进 HAVING。比如查“2025 年下单超 5 次的客户”,order_date >= '2025-01-01' 是行级条件,应放 WHERE:
- ✅ 正确:先用
WHERE order_date >= '2025-01-01'减少参与分组的数据量 - ❌ 错误:写成
HAVING order_date >= '2025-01-01'—— 这时order_date未出现在GROUP BY中,也不被聚合包裹,直接报错 - ⚠️ 危险:写成
HAVING MIN(order_date) >= '2025-01-01'虽能运行,但所有订单都参与了分组计算,白白多算一遍
典型安全组合:
SELECT customer_id, COUNT(*) AS order_cnt FROM orders WHERE status = 'completed' -- 先筛掉无效订单 GROUP BY customer_id HAVING COUNT(*) > 5; -- 再筛高频客户
HAVING 中能用什么,不能用什么
HAVING 可以引用的内容有限,容易踩坑:
- ✅ 聚合函数:
HAVING SUM(amount) > 1000、HAVING AVG(score) BETWEEN 70 AND 90 - ✅
SELECT中定义的别名(仅限 MySQL/PostgreSQL):HAVING total_sales > 5000(前提是 SELECT 里写了SUM(amount) AS total_sales) - ❌ 原始字段(未聚合也未出现在 GROUP BY 中):
HAVING user_name = 'Alice'会报错 - ❌ 表达式中混用非分组字段:
HAVING salary > 5000在没GROUP BY salary且没聚合它时非法
如果真要按某字段值筛选组,要么加进 GROUP BY(如 GROUP BY department, region),要么用聚合包裹(如 HAVING MAX(region) = 'North')。
性能敏感点:HAVING 条件越重,代价越高
因为 HAVING 是在分组完成之后才执行,所有分组都已生成并计算完聚合值,此时再丢弃大量组,等于白干。几个实际影响:
- 如果
HAVING COUNT(*) > 100筛掉 95% 的组,说明前面 95% 的分组计算全是浪费 - 多个聚合条件叠加(如
HAVING SUM(x) > 100 AND AVG(y) 10)会让优化器更难下推过滤 - 某些数据库(如旧版 MySQL)不支持在 HAVING 中用索引加速,纯内存比对
最稳妥的做法:能用 WHERE 缩小输入数据集的,绝不留给 HAVING 处理;HAVING 只保留真正依赖聚合结果的判断逻辑。










