CHAR_LENGTH函数按字符数而非字节数统计,结果取决于字符集:utf8mb4下中文汉字计为1字符,gbk等亦按字符单位计数;LENGTH则返回字节数,二者不可混用。

CHAR_LENGTH函数对中文字符的统计结果取决于字符集
CHAR_LENGTH函数返回的是字符串的字符数,不是字节数。但它的实际行为受数据库字符集影响:在utf8mb4或utf8字符集下,一个中文汉字算作1个字符;在gbk或latin1等单字节/双字节混合字符集下,只要字符能被正确解码,同样按字符计数——关键不是“中文”,而是字符集是否将该字符定义为单个可索引单位。
常见错误是看到CHAR_LENGTH('你好')返回2,就以为它“专门支持中文”,其实这只是因为UTF8编码中每个汉字占用3~4字节,但MySQL的CHAR_LENGTH在utf8mb4下仍按Unicode码点计数,不是按字节解析。
- 确认当前表字段字符集:
SHOW CREATE TABLE your_table,重点看DEFAULT CHARSET和列定义里的CHARSET - 不要用
LENGTH()代替CHAR_LENGTH()——LENGTH('你好')在utf8mb4下返回6(3字节×2),而CHAR_LENGTH('你好')返回2 - 如果字段是
latin1却存了UTF8编码的中文,CHAR_LENGTH会把乱码当多个无效字符处理,结果不可靠
CHAR_LENGTH与SUBSTRING配合截取中文时要注意边界
用CHAR_LENGTH做长度判断后调用SUBSTRING(str, pos, len),看似安全,但SUBSTRING本身也按字符位置切分,前提是字符集一致。一旦出现emoji或补充平面字符(如?),在utf8mb4下它们占1个字符位置,CHAR_LENGTH和SUBSTRING能协同工作;但在旧版MySQL(utf8mb4时,这类字符可能被截断或报错。
- 测试边界情况:
SELECT CHAR_LENGTH('a你好?'), SUBSTRING('a你好?', 2, 3)—— 应返回4和'你好?' - 避免在
utf8(非utf8mb4)字符集上处理4字节字符,否则CHAR_LENGTH可能返回异常值或截断 - 如果业务要求严格按视觉字数(比如短信字数限制),需额外过滤掉不可见控制字符,
CHAR_LENGTH不区分可见性
不同数据库里类似函数的行为差异
MySQL的CHAR_LENGTH、PostgreSQL的char_length()、SQL Server的LEN()都按字符计数,但SQL Server的LEN()会忽略末尾空格,而CHAR_LENGTH不会——这点在迁移脚本时容易出错。
- SQLite用
length(),行为等同于MySQL的CHAR_LENGTH - Oracle没有
CHAR_LENGTH,用LENGTH()(默认按字符),但需确认NLS_LENGTH_SEMANTICS设为CHAR而非BYTE - 如果跨库写通用SQL,别依赖函数名,优先用标准SQL的
CHARACTER_LENGTH(MySQL/PostgreSQL/Oracle均支持)
真正容易被忽略的点:排序规则(collation)不影响CHAR_LENGTH
很多人误以为utf8mb4_unicode_ci和utf8mb4_general_ci会影响CHAR_LENGTH结果,其实不会。排序规则只影响比较和排序,不改变字符计数逻辑。真正影响结果的是字符集本身,以及字符串是否被正确解码为有效字符序列。
最隐蔽的问题是:从客户端传入的字符串如果编码不匹配(比如用GBK发送UTF8字节流),MySQL可能将其当作非法序列部分替换为?,这时CHAR_LENGTH统计的是替换后的字符数,而非原始中文个数——这无法通过函数本身发现,必须从连接层排查。

















