sql聚合函数忽略null是三值逻辑设计的必然结果;count(*)统计所有行,count(列名)仅统计非null值;sum/avg等遇全null返回null,需coalesce兜底;left join后聚合须先coalesce再计算;where中用coalesce会导致索引失效。

SQL聚合函数忽略NULL不是bug,是按三值逻辑设计的必然结果;问题通常出在开发者把NULL当成“空”或“0”,然后在错误位置补默认值。
COUNT(*) 和 COUNT(列名) 的统计逻辑完全不同
很多人写 COUNT(status) 却以为在统计总订单数,结果比实际少了一半——因为 status 字段有大量NULL,而 COUNT(列名) 天然跳过它们。
-
COUNT(*):统计所有行,含全列为NULL的行 -
COUNT(列名):只统计该列非NULL的行数 - 查某列NULL数量?直接用
COUNT(*) - COUNT(列名) - 确认字段是否真为空,不能只看
COUNT(列名) = 0,要加WHERE 列名 IS NULL验证
SUM/AVG/MAX/MIN 返回 NULL 时下游代码会崩
整组数据全为NULL或 WHERE 无匹配行时,SUM()、AVG()、MAX() 都返回 NULL,而非数值0。JDBC用 getInt() 或 MyBatis 映射到 int 类型会抛 NullPointerException 或类型转换异常。
- 必须在外层兜底:
COALESCE(SUM(col), 0)(MySQL可用IFNULL(SUM(col), 0)) -
COALESCE(AVG(col), 0)只解决空结果问题,但无法修正因部分NULL导致的平均值偏高(比如10人中2人NULL,AVG只算8人) - 若业务要求“缺数据即0”,应在聚合前用
COALESCE(col, 0),而不是聚合后补0
LEFT JOIN 后直接聚合右表字段,结果凭空缩水
订单主表 LEFT JOIN 优惠券表后,SUM(coupon_discount) 比实际总优惠金额小——因为没用券的订单,coupon_discount 为 NULL,被 SUM 直接跳过。
- ✅ 正确:先清洗再聚合 →
SUM(COALESCE(coupon_discount, 0)) - ❌ 错误:后兜底 →
COALESCE(SUM(coupon_discount), 0),仍漏掉未匹配行 - 涉及多字段运算(如
total - discount),NULL会污染整条表达式,必须每列单独COALESCE:SUM(COALESCE(total, 0) - COALESCE(discount, 0))
WHERE 子句里用 COALESCE 就等于放弃索引
COALESCE、IFNULL 放进 WHERE 子句,几乎必然导致全表扫描——数据库无法利用 phone 字段上的索引。
- ❌
WHERE COALESCE(phone, '') = '138xxx'→ 全表扫描 - ✅ 筛选NULL请用
WHERE phone IS NULL - 想同时查NULL和指定值?拆成
WHERE phone = '138xxx' OR phone IS NULL,或建函数索引(MySQL 8.0+支持) -
ORDER BY COALESCE(created_at, '1970-01-01')同样会使索引失效
真正容易被忽略的点在于:NULL 不是“空”,也不是“0”,更不是“未填写”——它是“未知”。盲目用 COALESCE 填充,可能掩盖数据采集、ETL或业务逻辑层面的真实问题。











