SUBSTRING 默认按字符截取,但需配合 CHARACTER_LENGTH()(返回字符数)而非 LENGTH()(返回字节数),否则中文因 utf8mb4 占 3–4 字节而乱码;务必确保字段字符集为 utf8mb4、连接层编码一致。

MySQL 中 SUBSTRING 截取中文乱码?本质是字节 vs 字符混淆
直接说结论:SUBSTRING 默认按**字节**截取,而中文在 utf8mb4 下占 3–4 字节,用它配合 LENGTH()(返回字节数)截取多字节字符,必然丢半截字、出乱码。真正该配对使用的是 SUBSTRING + CHARACTER_LENGTH() —— 后者返回**字符数**,和人眼所见的“第几个字”一致。
SUBSTRING(str, pos, len) 的 pos 和 len 都按字符位置算
SUBSTRING 本身不区分编码,它的 pos 和 len 参数含义是“从第几个字符开始,取几个字符”,不是字节。但前提是:你传入的 pos 和 len 值本身得基于字符数,而非字节数。
- 错误写法(用
LENGTH()算起始位):SUBSTRING(name, LENGTH(LEFT(name, 2)), 1)→ 这里LEFT(name, 2)取前 2 字节,可能只截到半个汉字,LENGTH()返回值不可控 - 正确写法(用
CHARACTER_LENGTH()控制逻辑):SUBSTRING(name, 1, 2)直接取前 2 个字符;或动态计算:SUBSTRING(name, CHARACTER_LENGTH(name) - 2 + 1, 2)取后 2 字符 - 注意:
SUBSTRING在utf8mb4排序规则(如utf8mb4_0900_as_cs)下行为稳定;若用老式utf8(实际是 utf8mb3),部分四字节 emoji 仍会出错
CHARACTER_LENGTH() 不是万能补丁,得看字段实际编码
CHARACTER_LENGTH() 返回的是 Unicode 字符个数,但它依赖字段的字符集声明。如果字段定义为 latin1 却存了 UTF-8 编码的中文,函数会把一个中文当 3 个字符算 —— 这不是函数问题,是数据层编码错配。
- 检查字段真实字符集:
SHOW FULL COLUMNS FROM table_name LIKE 'column_name';,确认Collation对应utf8mb4_* - 临时转换安全截取(不改表结构):
SUBSTRING(CONVERT(column_name USING utf8mb4), 1, 5),但性能较差,别在大表 WHERE 中滥用 - 替代方案:
LEFT(column_name, 5)和SUBSTRING(column_name, 1, 5)行为完全一致,且更简洁;RIGHT()同理
遇到 SUBSTRING 返回空或问号?先查连接层编码
即使 SQL 写对了,客户端连上来用 latin1 或没设 SET NAMES utf8mb4,结果查出来仍是乱码或截断——这时 SUBSTRING 拿到的就是损坏的字符串,再怎么算字符数都白搭。
- 连接时强制指定:
mysql --default-character-set=utf8mb4 -u user -p - 执行前校验:
SELECT @@character_set_client, @@character_set_connection, @@character_set_results;三者都应为utf8mb4 - 常见错误现象:
SUBSTRING('你好世界', 1, 2)返回或空,大概率是客户端解码失败,不是函数失效
真正卡住人的从来不是函数怎么写,而是字符集声明、连接参数、字段存储这三层是否对齐。少一个 mb4,前面所有 CHARACTER_LENGTH() 都可能白算。


















