NULLIF是处理除零最直接的方案,因其将分母为0时转为NULL,使NULL/0返回NULL而不报错;聚合后除法需用NULLIF(分母, 0),注意顺序、字面量0及浮点精度问题。

为什么 NULLIF 是处理除零最直接的方案
因为 SQL 标准中,NULL / 0 不报错,结果是 NULL;而 10 / 0 会直接抛出 division by zero 错误。所以关键不是“捕获错误”,而是“提前让分母变成 NULL”。NULLIF(a, b) 的语义就是:当 a = b 时返回 NULL,否则返回 a。用它把分母中的 0 替换成 NULL,后续除法自然安全。
在 SUM 或 COUNT 聚合后做除法时怎么加 NULLIF
聚合函数本身不会触发除零,但聚合结果参与除法时才会。常见写法是把分母整个包进 NULLIF,且第二个参数必须是字面量 0(不能是表达式或列名,除非你明确想对比相等性):
SELECT SUM(sales) / NULLIF(COUNT(*), 0) AS avg_sales, SUM(profit) / NULLIF(SUM(cost), 0) AS margin_ratio FROM orders WHERE status = 'shipped';
注意点:
-
NULLIF(COUNT(*), 0)安全:COUNT(*)永远 ≥ 0,且只在无行时为 0,这时除法本就无意义 -
NULLIF(SUM(cost), 0)要小心:如果所有cost都是负数或 null,SUM(cost)可能为 0(比如-100 + 100),此时也会变NULL——这不是除零,而是业务逻辑歧义,得结合业务判断是否真要拦截 - 别写成
NULLIF(0, COUNT(*)),顺序反了就完全失效
和 CASE WHEN 相比,NULLIF 有什么实际差异
功能等价,但 NULLIF 更简洁、可读性更高,且多数数据库对它做了优化。例如下面两种写法结果一致:
-- 推荐:一行搞定 SUM(revenue) / NULLIF(SUM(expense), 0) <p>-- 等价但啰嗦 SUM(revenue) / CASE WHEN SUM(expense) = 0 THEN NULL ELSE SUM(expense) END
隐患在于:后者重复计算了 SUM(expense) 两次,可能影响性能(尤其子查询或窗口函数场景);前者只算一次。另外,CASE 容易漏写 ELSE 分支,导致意外 NULL 或报错。
不同数据库对 NULLIF 的兼容性和边界行为
NULLIF 是 SQL:2003 标准函数,PostgreSQL、SQL Server、Oracle、Snowflake、Trino 都支持;MySQL 从 5.7+ 支持;SQLite 支持。但要注意:
- BigQuery 不支持
NULLIF,得用IF(expression = 0, NULL, expression) - 如果分母是浮点数(如
DOUBLE),NULLIF(x, 0.0)可能因精度问题失效,稳妥做法是用范围判断:NULLIF(CAST(x AS DECIMAL), 0)或配合ABS(x) -
NULLIF(NULL, 0)返回NULL,符合预期;但NULLIF(0, NULL)返回0(因为0 = NULL为 unknown,不满足相等条件)
真正容易被忽略的是:当你把 NULLIF 用在嵌套聚合或窗口函数里时,执行顺序可能让分母“看起来是 0”实则已被过滤——务必在最终 SELECT 层做防御,而不是在子查询里提前替换。


















