count(列名)只忽略null,不忽略空字符串'',故count(status)将''计为有效值;业务上视''为“未填写”,但数据库视为合法非空值,导致统计高估。

聚合函数根本不忽略空字符串,只忽略 NULL——这是绝大多数统计偏差的真正起点。
为什么 COUNT(status) 会把 '' 当作有效值?
COUNT(列名) 统计的是该列「非 NULL」的行数,而空字符串 '' 是一个合法的、确定的字符串值,不是 NULL。数据库不会把它当作“空”跳过。
- 如果
status列包含'pending'、''、NULL、'done',那么COUNT(status)返回 3(''被计入,NULL被跳过) -
COUNT(*)返回 4(所有行,不管值是什么) - 业务上常把
''当作“未填写”,但数据库不认这个约定——语义错位就从这里开始
SUM / AVG / MAX 遇到 '' 会怎样?
空字符串不是数值,类型不匹配时直接报错,不是“被忽略”。比如对 VARCHAR 列执行 SUM(status),PostgreSQL 和 SQL Server 通常抛出类型转换错误;MySQL 在宽松模式下可能隐式转成 0,造成静默偏差。
-
MAX(status)在字符串列上正常比较:''按字典序排最前,NULL永远不参与比较 -
AVG(COALESCE(status, 'N/A'))没意义——COALESCE不会触发替换,因为''不是 NULL - 真正安全的做法是先用
NULLIF(status, '')把''转为 NULL,再进聚合
GROUP BY 里 '' 和 NULL 被当成两组?
是的。SQL 标准中,NULL 在 GROUP BY 中被视为相同值(所有 NULL 归为一组),而 '' 是普通字符串,和 ' '、'a' 一样独立分组。
- 如果
status列有''和NULL,GROUP BY status会产生至少两行结果 - 想统一语义,必须提前转换:
GROUP BY NULLIF(status, '') - 别在 WHERE 里写
status != '' OR status IS NOT NULL——它既不能走索引,也无法覆盖两种“空”的全部场景
最容易被绕过的点是:你写的 COUNT(status) 看似简单,但只要字段允许存 '',它就天然高估“有明确值”的行数。业务定义的“空”和数据库定义的“空”从来就不是一回事,靠默认行为永远得不到准确统计。











