GROUP BY 字符串分组结果受 NULL、空字符串、大小写、字符集和字段长度影响:NULL 单独成组,空字符串与 NULL 不等价;大小写敏感性由排序规则决定;COUNT(*) 包含 NULL 行,COUNT(name) 忽略 NULL;长字段建议用前缀或哈希优化。

GROUP BY 对字符串字段分组时,结果常和预期不符——不是语法错,而是 NULL、空字符串、大小写、长字段这些隐性因素在搅局。直接写 GROUP BY name 很容易漏掉关键细节。
NULL 和空字符串 '' 会被归为同一组吗?
不会。SQL 标准规定 NULL = NULL 为未知(UNKNOWN),所以多个 NULL 值在逻辑上不可比较;但几乎所有数据库(MySQL、PostgreSQL、SQL Server、Oracle)实际实现时,会把所有 NULL 归为一组——这是行为共识,不是标准保证。
而空字符串 '' 是确定值,和 NULL 默认不等价,因此会单独成组。
- 想让
NULL和''合并?用COALESCE(name, '')或CASE WHEN name IS NULL THEN '' ELSE name END - 想严格区分?不用处理,保持原样即可
- 验证当前行为:执行
SELECT name, COUNT(*) FROM t GROUP BY name;,看name列是否出现NULL单独一行
大小写敏感性怎么影响分组结果?
完全取决于字段的排序规则(collation)。比如 MySQL 中:utf8mb4_0900_as_cs 大小写敏感,utf8mb4_0900_ai_ci 不区分大小写也不区分重音。
- 查当前字段 collation:
SHOW FULL COLUMNS FROM table_name LIKE 'name';,看Collation列 - 临时强制区分大小写:
GROUP BY BINARY name(MySQL)、GROUP BY name COLLATE "C"(PostgreSQL) - 别在
GROUP BY里写LOWER(name)——除非你确认该字段没索引,否则会丢索引,全表扫描 - 字符集混用(如 UTF8 和 GBK)可能触发
Illegal mix of collations错误,直接报错
COUNT(*) 和 COUNT(name) 为什么差很多?
因为 COUNT(*) 统计行数,包含 NULL 行;COUNT(name) 只统计 name 非 NULL 的行——哪怕 name 是空字符串 '',只要不是 NULL,就算在内。
- 业务上“空字符串”也代表无效?用
COUNT(NULLIF(name, ''))(MySQL 8.0+/PostgreSQL) - 兼容老版本?写
SUM(CASE WHEN name != '' AND name IS NOT NULL THEN 1 ELSE 0 END) -
COUNT(DISTINCT name)同样忽略NULL,这点和COUNT(name)一致
对超长字符串(如 TEXT)直接 GROUP BY 很慢,怎么办?
长字段参与 GROUP BY 会导致内存中排序或哈希开销剧增,尤其当值重复率低时。
- 优先考虑前缀分组:
GROUP BY SUBSTR(name, 1, 50)(截取前 50 字符,需确认业务允许) - 更稳的方式是预计算哈希:
GROUP BY SHA2(name, 256),但注意哈希碰撞概率极低,可接受 - 建函数索引(MySQL 8.0+ / PostgreSQL):
CREATE INDEX idx_name_hash ON t (SHA2(name, 256)); - 避免在
WHERE条件里用SUBSTR(name, 1, 50) = ...后再GROUP BY name——这会让优化器放弃索引
NULL、没意识到的 collation、以及没测过的长字段性能拐点。写完 GROUP BY 语句后,先跑一句 SELECT name, COUNT(*) FROM t GROUP BY name ORDER BY COUNT(*) DESC LIMIT 10;,比什么都管用。

















