having必须跟在group by后,否则报错;其条件只能用group by列或聚合表达式;where用于分组前过滤,having用于分组后筛选;注意null、浮点精度及left join逻辑。

HAVING 必须跟在 GROUP BY 后面,单独写会直接报错 —— 这是绝大多数人第一次用就卡住的地方。
为什么一写 HAVING 就报 ERROR 1140?
MySQL 看到 HAVING 就默认你已经完成分组。没写 GROUP BY,它根本不知道“按什么分组后筛选”。错误信息通常是:Mixing of GROUP columns with no GROUP columns is illegal 或 In aggregated query without GROUP BY。
- 错误写法:
SELECT customer_id, SUM(amount) FROM orders HAVING SUM(amount) > 1000 - 正确写法:
SELECT customer_id, SUM(amount) FROM orders GROUP BY customer_id HAVING SUM(amount) > 1000 - 极少数合法但不推荐的例外:
SELECT COUNT(*) FROM log HAVING COUNT(*) > 1000(全表聚合,无分组),语义模糊、跨库不兼容,建议改用子查询
HAVING 条件里能用哪些字段?
HAVING 的上下文只有两类东西:出现在 GROUP BY 中的列,以及聚合表达式。原始行里的单值字段(比如 status、order_date)只要没进 GROUP BY,也没被 COUNT()、AVG() 包裹,就根本不可见。
- ✅ 允许:
HAVING COUNT(*) >= 3、HAVING AVG(price) > 100、HAVING category = 'tech'(前提是category在GROUP BY中) - ❌ 报错:
HAVING status = 'paid'(status未分组也未聚合)、HAVING order_date > '2026-01-01'(同理) - ⚠️ 别名陷阱:
SELECT customer_id, SUM(amount) AS total FROM orders GROUP BY customer_id HAVING total > 1000在 MySQL 8.0+ 默认关闭该特性,稳妥起见应写成HAVING SUM(amount) > 1000
WHERE 和 HAVING 混用时,条件该放哪?
执行顺序是硬约束:WHERE → GROUP BY → HAVING。把本该提前过滤的条件塞进 HAVING,等于让数据库先全量分组、再丢弃,纯属浪费 CPU 和内存。
- 放
WHERE(分组前筛行):created_at >= '2026-01-01'、status = 'active'、amount > 0 - 放
HAVING(分组后筛组):COUNT(*) >= 5、SUM(amount) > 5000、MAX(updated_at) > '2026-06-01' - LEFT JOIN 场景要特别小心:
HAVING COUNT(o.id) >= 3会把零订单用户全踢掉;如果业务要求“保留所有用户”,就得用条件聚合(如SUM(CASE WHEN o.id IS NOT NULL THEN 1 ELSE 0 END))或窗口函数
NULL 和聚合精度容易导致漏判
聚合结果为 NULL(比如某组所有 price 都是 NULL,求 AVG(price))时,HAVING AVG(price) > 100 会直接跳过该组——因为 NULL > 100 判为 UNKNOWN,不满足 TRUE。
- 稳妥写法:
HAVING AVG(price) IS NOT NULL AND AVG(price) > 100 - 想查“没有任何有效记录的用户”,别用
HAVING COUNT(status) = 0(它跳过全为NULL的组),改用HAVING COUNT(status) = 0 OR COUNT(*) = COUNT(status)或显式判断status IS NULL - 浮点聚合(如
AVG())可能因精度误差导致比较失败,必要时加ROUND()或用区间判断
最常被忽略的是执行顺序带来的性能隐含成本:HAVING 不是“加个条件而已”,它是在所有分组计算完之后才跑的。数据量稍大,GROUP BY 本身又没索引支撑,整个查询就会明显变慢 —— 这时候回过头看 WHERE 是否还能再压一层,往往比调优 HAVING 更有效。











