MySQL的LEFT()和RIGHT()按字符截取,而非字节;但若字段为VARBINARY类型或字符集不一致,会退化为字节截取,导致乱码或数据丢失。

MySQL的LEFT()和RIGHT()按字节还是按字符截取?
按字符截取,不是字节——但前提是字符集是多字节时(比如utf8mb4),你得确认字段实际存储的是什么编码,否则看似“截对了”,实则丢数据。
这两个函数在MySQL里始终以“字符”为单位操作,不管底层是1字节的latin1还是3–4字节的utf8mb4。也就是说:LEFT('你好', 1)返回'你',不是前1个字节(那会是乱码)。
常见错误现象:
• 在utf8mb4表中用LEFT(name, 10)做索引前缀,结果查不到预期记录 → 实际可能是前10个字符占了30+字节,而索引长度限制按字节算
• 把LEFT()结果直接拼进日志或URL,遇到emoji时被截成半个字符 → 显示或报错
- 使用场景:生成摘要、取文件名前缀、构造短标识符
- 参数差异:
LEFT(str, len)中len是字符数,不是字节数;不支持负数长度 - 性能影响:纯内存计算,无IO开销,但若
str是大字段(如TEXT),每次调用仍需加载完整值再截取
PostgreSQL里LEFT()和RIGHT()的行为完全不同
PostgreSQL没有内置LEFT()或RIGHT()函数,直接调用会报错:ERROR: function left(unknown, integer) does not exist。
必须用substring()或substr()模拟:
SELECT substring('hello世界', 1, 5); -- 返回 'hello'注意:substring(str FROM start FOR len)里的start和len也都是字符位置,不是字节。PostgreSQL默认按Unicode字符计数,对emoji、中文、英文一视同仁。
- 容易踩的坑:误写
LEFT(col, 5)导致SQL报错,尤其从MySQL迁移过来时 - 兼容性影响:如果应用层硬编码了
LEFT(),换库时必须改SQL或加兼容函数 - 替代方案:
left(str, len)可通过创建自定义函数实现,但需显式声明LANGUAGE sql并处理空值
想真正按字节截取?别碰LEFT()/RIGHT()
所有主流SQL引擎的LEFT()/RIGHT()都不提供字节级控制。真要按字节切(比如适配旧协议、拼接二进制头),得绕道:
- MySQL:用
SUBSTR(<code>col, 1, 10) +CONVERT(... USING latin1)强制转单字节编码(风险极高,会丢信息) - PostgreSQL:用
convert_from(substring(col::bytea, 1, 10), 'UTF8'),但要求原字段是bytea类型 - 更安全的做法:把截取逻辑移到应用层,用Python/Go等语言的bytes切片,明确控制字节边界
典型错误现象:SUBSTR(col, 1, 10)在utf8mb4下截出半个emoji → 前端渲染失败或数据库报Incorrect string value
字符集设置如何悄悄影响LEFT()结果?
影响不在函数本身,而在字段定义和连接字符集。例如:
建表时用CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,但客户端连接用SET NAMES latin1,这时LEFT(col, 5)拿到的其实是乱码后的字符序列,截取结果完全不可控。
- 检查方法:
SHOW VARIABLES LIKE 'character_set%';+SHOW CREATE TABLE t; - 关键配置项:
character_set_client、character_set_connection、character_set_database三者必须一致,否则函数输入已是失真字符串 - 最容易被忽略的一点:即使表和连接都设对了,如果字段是
VARBINARY类型,LEFT()会按字节解释内容,此时行为突变 —— 它不再“按字符”,而是按原始字节流截取
所以别只盯着函数看,先盯住字段类型和三层字符集是否咬合。一个VARBINARY字段混在VARCHAR堆里,LEFT()就可能突然变成字节切割器,而且不会报错。










