having必须紧跟group by,否则报错;其条件只能用group by列或聚合表达式;where用于分组前过滤行,having用于分组后筛选组;null和浮点精度需特别处理。

HAVING 不能单独用,必须紧跟 GROUP BY;否则直接报错 —— 这是写错的第一高发点。
GROUP BY 必须出现在 HAVING 之前
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 条件里只能用 GROUP BY 列或聚合表达式
HAVING 的上下文里没有原始行数据,只有两类东西:被 GROUP BY 显式列出的列,以及 COUNT()、SUM()、AVG() 这类聚合结果。其他字段哪怕在 SELECT 里出现,只要没进分组也没被聚合,就不可用于 HAVING。
- ✅ 允许:
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 混用时,条件放哪取决于执行时机
SQL 执行顺序是硬约束: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,不满足真值条件。
- 浮点聚合(如
SUM(decimal_col))可能因精度误差导致>=判断失败,必要时加ROUND()或用区间判断(如BETWEEN) -
HAVING对NULL敏感,若业务允许包含空聚合组,需显式补OR AVG(price) IS NULL类逻辑
真正容易被忽略的不是语法,而是执行顺序带来的性能隐含成本:一个本该在 WHERE 里拦住的千万行数据,如果错放到 HAVING,分组过程本身就会吃光内存和时间。别只盯着“能不能跑通”,得看“为什么非得这么跑”。











