having必须配合group by使用,因其作用对象是分组后的聚合结果而非原始行;无group by时,数据库无法确定分组依据,多数引擎会报错,且隐式单组行为跨版本不一致。

HAVING 必须跟在 GROUP BY 后面,且只能用于过滤分组后的聚合结果;把本该写在 WHERE 的条件塞进 HAVING,轻则性能暴跌,重则直接报错。
HAVING 为什么必须配合 GROUP BY 使用
因为 HAVING 的作用对象是“组”,不是行。没有 GROUP BY,数据库就不知道你在筛什么“组”——整张表会被当成一个隐式分组,但这种行为跨版本、跨引擎不一致,MySQL 8.0+ 默认拒绝执行 HAVING 无 GROUP BY 的语句(除非显式写 GROUP BY (),但没人这么干)。
- 错误写法:
SELECT COUNT(*) FROM orders HAVING COUNT(*) > 100→ 多数 MySQL 实例会报Unknown column 'COUNT(*)' in 'having clause'或触发ONLY_FULL_GROUP_BY拒绝 - 正确写法:
SELECT COUNT(*) FROM orders GROUP BY () HAVING COUNT(*) > 100(仅用于极少数单值校验场景,不推荐) - 真正该用的场景:按字段分组后筛组,比如
GROUP BY dept,再用HAVING AVG(salary) > 15000
WHERE 和 HAVING 混用时的顺序与分工
SQL 执行顺序固定为 FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY。这意味着 WHERE 先砍掉大量无关行,GROUP BY 再基于剩余数据分组,HAVING 最后对聚合结果做精筛。
- 想查“2024 年之后下单、且人均订单额超 5000 的客户”:WHERE 筛时间,GROUP BY 分客户,HAVING 算均值
→SELECT customer_id, AVG(amount) AS avg_amt FROM orders WHERE order_date > '2024-01-01' GROUP BY customer_id HAVING avg_amt > 5000 - 反例:把
order_date > '2024-01-01'放 HAVING → 先全表分组再丢弃,CPU 和内存白耗 - WHERE 中不能用聚合函数(
COUNT(*) > 5语法非法),HAVING 中不能引用未分组未聚合的原始字段(HAVING user_name = 'Alice'报错,除非user_name在GROUP BY里)
HAVING 中使用别名的兼容性陷阱
MySQL 允许在 HAVING 中直接用 SELECT 定义的别名(如 avg_sal),但这是 MySQL 特有的宽松行为;标准 SQL 和 PostgreSQL、SQL Server 等要求必须写完整表达式。生产环境若需迁移或兼容,建议统一用原始表达式。
- 安全写法:
HAVING AVG(salary) > 15000 - 风险写法:
HAVING avg_sal > 15000(依赖 MySQL 的非标准扩展,升级或换库可能失效) - 浮点精度坑:若 SELECT 中用了
ROUND(AVG(score), 2) AS avg_score,HAVING 也得写ROUND(AVG(score), 2) > 4.5,否则因计算路径差异导致判断失败
ONLY_FULL_GROUP_BY 开启后常见报错及修复
MySQL 5.7+ 默认启用 ONLY_FULL_GROUP_BY,它强制要求:SELECT 列要么出现在 GROUP BY 中,要么被聚合函数包裹。否则 HAVING 可能根本跑不起来,或者返回不可靠的随机值。
- 典型报错:
Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'db.users.name' which is not functionally dependent on columns in GROUP BY clause - 修复方式三选一:
— 删掉非分组非聚合字段(如去掉name)
— 加进 GROUP BY(GROUP BY city, name)
— 用聚合函数包裹(MAX(name)或ANY_VALUE(name),后者需确认业务接受任意值) - 临时关闭(不推荐):
SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));—— 这会让查询结果失去确定性
最易被忽略的一点:HAVING 本身不走索引,所有过滤都在内存中完成。所以它不是性能优化手段,而是业务逻辑兜底工具——前期靠 WHERE 缩小数据集,后期靠 HAVING 表达聚合规则,顺序不能颠倒,字段引用不能越界。











