count(列名)只忽略null不忽略空字符串'',导致统计高估;应使用count(nullif(列,''))统一过滤二者,分组时也需先转换语义。

聚合函数只忽略 NULL,不忽略空字符串('')——这是绝大多数统计偏差的根源。
为什么 COUNT(status) 会把 '' 当作有效值?
COUNT(列名) 统计的是该列「非 NULL」的行数,而空字符串 '' 是一个合法的非空值,会被计入。比如 status 列包含 'pending'、''、NULL、'done',那么 COUNT(status) 返回 3,COUNT(*) 返回 4。
-
COUNT(*)数所有行,不管列值是''还是NULL -
COUNT(status)跳过NULL,但保留'' - 业务上常把
''当作“未填写”,但数据库不认这个约定,直接用COUNT(status)就会高估有效数据量
如何统一过滤 NULL 和 ''?用 NULLIF() 转换语义
别在 WHERE 里写 status != '' 或 status IS NOT NULL 单独判断——它们无法同时覆盖两种“空”状态,且 != '' 不能走索引。
- 用
NULLIF(status, ''):当status = ''时返回NULL,否则返回原值;原本就是NULL的也保持为NULL - 再配合
COUNT(NULLIF(status, '')),就能真正统计“有明确状态”的行数 - 分组时也应先转换:
GROUP BY NULLIF(status, ''),避免''和NULL被当成两组
SUM/AVG/MAX 等函数遇到 '' 会怎样?
空字符串本身不是数值,类型不匹配时直接报错,不是“被忽略”。例如对 VARCHAR 列执行 SUM(status),多数数据库会抛出类型转换错误,而不是跳过或转成 0。
-
SUM(列名)要求列是数值型,否则报错;''不可能存进INT列,所以实际场景中它只出现在字符串列 - 若你用
CAST(status AS INT)强转,''在不同数据库行为不一:MySQL 可能转成 0,PostgreSQL 直接报错,SQL Server 可能转成 0 或失败 -
MAX(status)在字符串列上正常比较,''按字典序排最前,NULL永远不参与比较
COALESCE 放在哪一层才真正起作用?
COALESCE 必须包裹整个聚合结果,而不是塞进聚合函数内部。放错位置会导致语义完全跑偏。
- 正确:
COALESCE(SUM(amount), 0)—— 整个结果集为空时返回 0 - 错误:
SUM(COALESCE(amount, 0))—— 把每个NULL当 0 加进去,改变业务含义 - 分组场景下,
COALESCE必须在外层:SELECT dept, COALESCE(AVG(salary), 0),而非AVG(COALESCE(salary, 0)) -
COUNT(*)永远不会返回NULL(空组返回 0),不需要COALESCE
真正容易被绕过的点是:业务代码和数据库对“空”的定义不一致。只要字段允许存 '',就必须主动用 NULLIF() 或显式条件去对齐语义,靠默认聚合行为永远得不到准确统计。











