HAVING必须紧跟GROUP BY,否则报错;其条件仅限GROUP BY列或聚合表达式;WHERE用于分组前过滤行,HAVING用于分组后筛选组;NULL和浮点精度需特别处理。

HAVING 必须跟在 GROUP BY 后面,否则直接报错
直接写 HAVING SUM(amount) > 1000 而不带 GROUP BY,MySQL 会抛出 ERROR 1140: In aggregated query without GROUP BY。因为 HAVING 的作用对象是“每个分组聚合出来的一行”,不是原始数据行——没有分组,数据库根本不知道“按什么来聚合后筛选”。
- 正确顺序只能是:
SELECT→FROM→WHERE→GROUP BY→HAVING→ORDER BY - 漏掉
GROUP BY是新手最常踩的语法坑,不是逻辑问题,是硬性语法限制 - 哪怕你只想查全表聚合值(比如全表平均金额),也不该用
HAVING;它天生为“多组结果”而生,单组无意义
WHERE 和 HAVING 混用时,过滤时机决定结果和性能
WHERE 在分组前筛原始行,HAVING 在分组后筛组。把本该放 WHERE 的条件错塞进 HAVING,轻则慢,重则逻辑错或报错。
- 日期、状态等单行可判断的条件(如
order_date > '2023-01-01')必须放WHERE:提前减少扫描和分组的数据量 - 聚合结果条件(如
COUNT(*) >= 5、AVG(amount) > 300)只能放HAVING:因为分组前这些值还不存在 - 把
status = 'active'写进HAVING会报错——除非status在GROUP BY中,否则分组后它不唯一,数据库拒绝引用
HAVING 中能用哪些列?只认 GROUP BY 列和聚合表达式
HAVING 的可见列范围极窄:要么是 GROUP BY 中明确列出的列,要么是聚合函数(SUM()、COUNT()、AVG() 等)。其他列一律不可用。
- 错误示例:
HAVING order_date > '2023-01-01'——order_date没在GROUP BY里,也不是聚合值,MySQL 直接报Unknown column - 正确写法:如果真要按日期分组后再筛,得先
GROUP BY DATE(order_date),再在HAVING中用该表达式 - MySQL 8.0+ 允许在
HAVING中用SELECT别名(如HAVING total > 1000),但 PostgreSQL/SQL Server 不支持,跨库迁移时建议写成HAVING SUM(amount) > 1000
LEFT JOIN + HAVING 容易误删 NULL 行
用 LEFT JOIN 关联后加 HAVING COUNT(o.id) >= 3,会把所有没订单的用户(o.id 为 NULL)整组踢掉——因为 COUNT(o.id) 对 NULL 计数为 0,不满足条件。
- 如果你本意是“保留所有用户,只对高频用户做统计”,
HAVING就不合适;它本质是“减法操作”,只留满足条件的组 - 替代方案一:用条件聚合绕过,比如
SUM(CASE WHEN o.id IS NOT NULL THEN 1 ELSE 0 END) >= 3放在SELECT里,再用WHERE或外层查询筛 - 替代方案二:用窗口函数,如
COUNT(*) OVER (PARTITION BY u.id),避免分组丢失行
真正难的不是语法,而是想清楚“这个条件该在分组前判断,还是分组后判断”。一个 WHERE 写成 HAVING,可能让查询变慢几倍;一个 HAVING 写成 WHERE,直接报错。执行顺序不是理论,是每一行 SQL 跑起来的实际路径。


















