SELECT col1/col2*100报“除零错误”是因为数据库运行时直接计算,不检查分母是否为零;用NULLIF(SUM(col2),0)可将0转为NULL,避免报错,且结果为NULL而非崩溃。

为什么 SELECT col1/col2*100 会报“除零错误”?
因为数据库在执行除法前不检查分母是否为零,只要 col2 出现 0 或 NULL,就会直接抛出错误(如 SQL Server 的 Msg 8134,PostgreSQL 的 division by zero)。这不是语法问题,而是运行时计算失败。
常见场景包括:统计各渠道转化率、订单完成率、库存使用率等分母可能为零的聚合指标。
- 分母字段本身允许
NULL或业务逻辑中确实存在0值(比如某天无下单量) - 用
SUM()聚合后分母为0,例如SUM(order_count) = 0 -
COALESCE(col2, 0)不能解决——它把NULL变成0,反而加剧除零
用 NULLIF() 是最简洁安全的写法
NULLIF(expr1, expr2) 在两个表达式相等时返回 NULL,否则返回 expr1。把它套在分母上,就能让 0 自动转成 NULL,而任何数除以 NULL 的结果也是 NULL(不会报错)。
示例(SQL Server / PostgreSQL / MySQL 8.0+):
SELECT
channel,
SUM(conversions) AS conv,
SUM(impressions) AS imp,
ROUND(
CAST(SUM(conversions) AS FLOAT) * 100 / NULLIF(SUM(impressions), 0),
2
) AS rate_pct
FROM campaign_log
GROUP BY channel;
- 必须把分子显式转为浮点型(如
CAST(... AS FLOAT)或1.0 * ...),否则整数除法会截断小数 -
NULLIF(SUM(impressions), 0)确保分母为0时整个除法结果为NULL,而非崩溃 - MySQL 5.7 及更早版本不支持
NULLIF()在所有上下文中使用,需改用CASE
兼容旧版 MySQL 或需要自定义默认值时用 CASE WHEN
当你要把除零结果统一显示为 0、-1 或 'N/A',而不是 NULL,CASE 更灵活。
示例(全版本兼容):
SELECT
dept,
COUNT(*) AS total,
COUNT(CASE WHEN status = 'done' THEN 1 END) AS done,
CASE
WHEN COUNT(*) = 0 THEN 0
ELSE ROUND(100.0 * COUNT(CASE WHEN status = 'done' THEN 1 END) / COUNT(*), 2)
END AS done_rate
FROM tasks
GROUP BY dept;
- 注意顺序:先判断分母是否为
0,再做除法;条件写反会导致仍报错 - 避免写成
CASE WHEN COUNT(*) > 0 THEN ... ELSE 0 END—— 效果一样,但可读性略差 - 如果字段本身含
NULL,记得在COUNT()外层补COALESCE或在CASE中额外判断
视图定义里最容易被忽略的三个细节
视图一旦创建,内部逻辑就固化了。线上出错往往不是查询时写的不对,而是建视图时没兜住边界。
- 别在视图里裸写
col_a / col_b—— 即使当前数据没零,业务变化后可能引入 - 聚合视图(
GROUP BY)中,分母用SUM()或COUNT()后必须套NULLIF或CASE,不能依赖上游过滤 - Oracle 用户注意:
DECODE可替代CASE,但NULLIF行为一致;而NVL不适用于防除零,它不能把0转成NULL
真正麻烦的从来不是写一行 NULLIF,而是有人在视图里留了个 /,半年后数据突变,凌晨三点告警响起来。










