coalesce(sum(col), 0) 是最安全兜底写法,用于将聚合结果 null 转为 0;不可写成 sum(coalesce(col, 0)),否则扭曲语义;coalesce 不生成空组,需维表 left join 补全;空字符串与 null 语义不同,混用导致统计偏差。

COALESCE(SUM(col), 0) 是最常用且安全的兜底写法
当 SUM、AVG 或 MAX 返回 NULL,通常是因为整组无数据(如 WHERE 条件未匹配任何行)或该组所有值恰好都是 NULL。这不是错误,而是 SQL 标准行为,用来区分“无数据”和“数据为零”。直接在应用层调用 getInt() 或前端渲染会出错。
正确做法是把 COALESCE 套在聚合函数**外层**:COALESCE(SUM(amount), 0)。它只在聚合结果为 NULL 时替换为 0,不影响原有聚合逻辑。
- 别写成
SUM(COALESCE(amount, 0))——这会把每行NULL当 0 算,结果可能虚高(比如统计“有订单用户数”,误把没下单的人也算进总额) -
COALESCE参数类型必须兼容:COALESCE(AVG(score), 0)合法,但COALESCE(MAX(name), 0)会报类型冲突 - MySQL 用户可用
IFNULL(SUM(col), 0),但跨库迁移建议统一用COALESCE
SUM(col1 - col2) 少算?问题不在 SUM,在运算表达式本身
SUM 没问题,问题出在 col1 - col2 这个标量运算上。只要 col1 或 col2 任一为 NULL,整行结果就是 NULL,被 SUM 自动跳过——你不是漏了求和,是这一行根本没参与计算。
典型场景:订单表 LEFT JOIN 优惠券表后,coupon_discount 为 NULL 表示“没用券”,业务上应计为 0。
- 错误:
SUM(total - discount)→ 第 3 行因discount为NULL,整行变NULL,彻底消失 - 正确:
SUM(COALESCE(total, 0) - COALESCE(discount, 0)) - 更稳妥:先确认
discount IS NULL的语义——是“未使用”还是“金额待确认”?后者补 0 会歪曲事实
GROUP BY 后某组显示空白?COALESCE 解决不了“空组”问题
COALESCE(SUM(amount), 0) 能让已有分组的 NULL 变成 0,但它**不会凭空造出行**。如果某部门在数据中完全没出现(比如本月无订单),GROUP BY dept 就不会生成该部门的记录,COALESCE 根本没机会执行。
想让“无数据的组”也显示 0,必须靠数据源补全:
- 用维表
LEFT JOIN:比如先有departments全量表,再左连订单聚合结果 - 用
UNION ALL补缺行(小规模场景) - 应用层做兜底(不推荐,破坏 SQL 单一职责)
空字符串 '' 和 NULL 混用会导致统计口径撕裂
SUM、COUNT(col) 只忽略 NULL,不忽略空字符串 ''。但业务代码常把两者都当作“空”,造成前后端统计不一致。
例如:COUNT(status) 会把 '' 计入,但 WHERE status IS NULL 查不到它;SUM(LEN(status)) 中 '' 长度为 0,而 NULL 仍被跳过。
- 查真正缺失的数据,得分开写:
WHERE status IS NULL OR status = '' - 清洗时注意语义:
COALESCE(status, '')把NULL转成'',但status = ''可能代表“已填但留空”,和NULL含义不同 - 同一列里混着“未发生”“不适用”“录入失败”三种
NULL,统一COALESCE(col, 0)很危险
复杂点从来不在语法,而在你是否清楚每一处 NULL 在业务里到底代表什么。











