length(str) - length(replace(str, 'x', '')) 可计算单字节ascii字符频率,但不支持正则、大小写敏感性由collation决定,对多字节字符(如é、emoji)及子串统计不可靠,应依数据库版本选用regexp_count等unicode感知函数。

LENGTH(str) - LENGTH(REPLACE(str, 'x', '')) 能算单字符频率,但别直接套用
这个公式本质是“原长减去删掉目标字符后的长度”,对单个 ASCII 字符(比如 'a'、' ')确实有效。但要注意:它只统计**完全匹配的字符**,不支持正则、不区分大小写、也不处理多字节字符的边界问题。
常见错误现象:LENGTH('café') - LENGTH(REPLACE('café', 'e', '')) 在 MySQL 5.7 + utf8mb4 下返回 1(正确),但在某些旧配置或 SQL Server 中可能因编码截断返回 0 —— 因为 'é' 是两个字节,而你写的 'e' 是单字节,根本没匹配上。
- 使用场景:快速查日志字段里某个分隔符(如
'|')出现几次,或统计邮箱里的'@'个数 - 参数差异:
REPLACE()第三个参数不能为空字符串以外的值;若写成REPLACE(str, 'x', NULL),整行结果变NULL - 性能影响:对大文本(>1MB)字段反复计算会明显拖慢查询,尤其在 WHERE 或 ORDER BY 中用它
想统计多个字符或子串?别硬套 LENGTH/REPLACE,换函数
比如要统计 'ab' 在 'abababc' 中出现几次,LENGTH(str) - LENGTH(REPLACE(str, 'ab', '')) 除以 2 是错的——因为 REPLACE() 是贪婪替换,'abab' 会被一次性替换成空,只算作一次删除,不是两次。
正确做法取决于数据库:
- PostgreSQL:用
regexp_replace()配合array_length(string_to_array(...), 1),或直接regexp_count()(v15+) - MySQL 8.0+:用
REGEXP_COUNT(<code>str,'ab') - SQL Server:得用递归 CTE 或自定义函数,原生没轻量方案
- 兼容性兜底:如果只能用老 MySQL,先用
REPLACE(str, 'ab', 'x'),再用LENGTH()差值除以 2,但必须确保'x'不在原文中出现
大小写敏感吗?看 COLLATION,不是看函数本身
REPLACE() 是否忽略大小写,完全由字段或字符串字面量的排序规则(COLLATION)决定,跟函数无关。比如 REPLACE('ABC', 'b', '') 在 utf8mb4_0900_as_cs 下不替换,在 utf8mb4_0900_ai_ci 下会把 'B' 当 'b' 删掉。
容易踩的坑:
- 建表时没显式指定 COLLATION,依赖默认值,上线后在不同环境行为不一致
- 用
CONVERT(<code>strUSING utf8mb4) 改编码但没改 COLLATION,大小写逻辑还是旧的 - 临时想忽略大小写?加
COLLATE utf8mb4_0900_ai_ci,例如:REPLACE(str COLLATE utf8mb4_0900_ai_ci, 'a', '')
中文、emoji、组合字符怎么算?优先走 Unicode-aware 方案
用 LENGTH() 算 '??'(程序员 emoji)里 '?' 的出现次数?别试。这个 emoji 是多个码点组合的,REPLACE() 根本无法精准切分,而且 LENGTH() 返回的是字节数(在 utf8mb4 下可能是 8~12 字节),不是字符数。
真正靠谱的做法:
- MySQL 8.0+:用
CHAR_LENGTH()替代LENGTH(),它返回字符数而非字节数;再配合REGEXP_REPLACE()做 Unicode 感知替换 - PostgreSQL:
length()默认就是字符长度,且regexp_replace()支持u标志处理 Unicode - 别依赖 SUBSTRING + 循环遍历:性能差、易出界、组合字符(如带声调的汉字)会切错
复杂点在于:同一个视觉字符,在不同数据库、不同版本、不同 COLLATION 下,“是不是一个字符”这件事本身就没标准答案。动手前先 SELECT CHARSET(col), COLLATION(col) 看清楚。










