where不能过滤分组后的零值,因为它在group by之前执行,无法访问sum()、count()等聚合结果;having专用于分组后筛选,必须用它处理聚合条件,如having sum(sales) > 0,并注意null与0的区别及除零防护。

为什么WHERE不能过滤分组后的零值
因为 WHERE 在分组前执行,它看到的是原始行,不是 SUM()、COUNT() 算出来的聚合值。比如你想剔除 SUM(sales) = 0 的分组,写成 WHERE SUM(sales) > 0 会直接报错:「invalid use of aggregate function」。
HAVING 是唯一正确的入口点
HAVING 专为过滤分组结果而设,它作用于 GROUP BY 之后的聚合结果上。只要条件里包含聚合函数,就必须用 HAVING。
- 想剔除总销售额为 0 的品类:
HAVING SUM(sales) > 0 - 想保留至少有 5 笔订单的客户:
HAVING COUNT(*) >= 5 - 要排除平均评分低于 3 的产品:
HAVING AVG(rating) >= 3
注意:HAVING 必须紧跟在 GROUP BY 后,不能放在 ORDER BY 或 SELECT 前面。
多个聚合字段同时为零怎么判断
常见场景是剔除「销量=0 且利润=0 且成本=0」的整行分组。别用 WHERE sales = 0 AND profit = 0 AND cost = 0——那是错的,而且 WHERE 根本不认这些别名。
正确写法是:
SELECT category,
SUM(sales) AS sales,
SUM(profit) AS profit,
SUM(cost) AS cost
FROM orders
GROUP BY category
HAVING NOT (SUM(sales) = 0 AND SUM(profit) = 0 AND SUM(cost) = 0);
等价但更直白的写法:HAVING SUM(sales) != 0 OR SUM(profit) != 0 OR SUM(cost) != 0。
小心 NULL 和 0 的混淆
SUM() 对空组返回 NULL,不是 0。所以 HAVING SUM(x) > 0 会漏掉那些本该算作「0」但实际是 NULL 的组(比如某品类根本没数据)。
如果业务上要把 NULL 当 0 处理再比较,得先兜底:
HAVING COALESCE(SUM(sales), 0) > 0
但要注意:这会让原本无数据的分组也被纳入计算,是否符合业务意图需确认。多数情况下,空组本就不该出现在结果里,HAVING 自然就把它筛掉了。
真正容易被忽略的是:HAVING 条件里一旦涉及除法(如 SUM(revenue)/SUM(cost)),必须先用 NULLIF(SUM(cost), 0) 防除零,否则整个查询直接中断——这个错误不会只影响某一行,而是让整个结果集消失。











