空字符串''与NULL在GROUP BY中默认不合并,被视为两个独立值;需用COALESCE(col, '')或CASE WHEN显式统一,否则统计结果会分离。

空字符串和NULL在GROUP BY中默认不合并
直接写 GROUP BY col 时,''(空字符串)和 NULL 会被当成两个完全不同的值,各自成组。这不是 bug,是 SQL 标准行为——NULL = NULL 为 unknown,而 '' = '' 为 true,二者不可互换。
常见错误现象:统计“未填写城市”的用户数时,发现 WHERE city IS NULL 和 WHERE city = '' 返回不同结果;分组后看到两行都标着“城市为空”,但其实是分开的两组。
- 显式合并最稳:用
COALESCE(col, '')把NULL转成'',或用CASE WHEN col IS NULL THEN '' ELSE col END - 若想把两者都归为
'unknown',统一写成COALESCE(NULLIF(col, ''), 'unknown') - 别依赖数据库默认行为——MySQL 在某些 sql_mode 下可能表现不一致,PostgreSQL 则严格区分
用COUNT(*)和COUNT(col)区分“有值”和“非空行”
COUNT(*) 统计所有行,COUNT(col) 自动跳过 NULL,但**不跳过空字符串**。这意味着:'' 会被 COUNT(col) 算作“有效值”——这往往不是你想要的。
- 要统计“真正有内容的字段数”,用
COUNT(NULLIF(col, ''))(MySQL 8.0+/PostgreSQL) - 兼容旧版 MySQL 或需更明确逻辑时,写
SUM(CASE WHEN col != '' AND col IS NOT NULL THEN 1 ELSE 0 END) -
COUNT(DISTINCT col)同样忽略NULL,但保留''作为一个独立值
大小写与排序规则影响分组边界
如果字段 collation 是 utf8mb4_0900_ai_ci(不区分大小写、不区分重音),那么 'ABC' 和 'abc' 会被归为同一组;但 '' 仍和它们无关——它只受自身是否为空影响。
- 查看当前列排序规则:
SHOW FULL COLUMNS FROM table_name LIKE 'col_name';,看Collation列 - 临时强制区分大小写分组:
GROUP BY BINARY col(MySQL)或GROUP BY col COLLATE "C"(PostgreSQL) - 不要在
GROUP BY中写LOWER(col),除非已建函数索引,否则必然全表扫描
字符串拼接时空值处理要提前做
像 GROUP_CONCAT()(MySQL)或 STRING_AGG()(PostgreSQL/SQL Server)这类函数,默认会跳过 NULL,但**不会跳过 ''**——空字符串照样参与拼接,可能导致结果里出现多余逗号或分隔符。
- 安全写法:先用
NULLIF(col, '')把空字符串转成NULL,再进聚合,如GROUP_CONCAT(NULLIF(tag, '')) - 需要保留空字符串但不想让它“占位”,可加条件过滤:
GROUP_CONCAT(CASE WHEN col != '' THEN col END) - 窗口版
STRING_AGG() OVER (PARTITION BY ...)同样受此规则约束,顺序和空值处理必须显式声明
空字符串不是 NULL,也不等于 NULL;它是个合法的、可索引的、参与比较和拼接的字符串值——这个认知偏差,是绝大多数分组统计出错的起点。










