应优先使用left或right截取固定长度的前缀或后缀,因其更安全、可读性更强;substring适用于复杂位置截取,但需注意负索引规则及越界风险;按分隔符切分应选用substring_index而非嵌套locate+substring。

MySQL 里截取字符串,SUBSTRING 是最灵活的,但不是所有场景都得用它;LEFT 和 RIGHT 在明确只取头尾时更直白、可读性更强,也更容易避免越界错误。
什么时候该用 LEFT 或 RIGHT 而不是 SUBSTRING
当你要取固定长度的前缀或后缀,且起始位置恒为 1 或末尾时,LEFT(str, n) 和 RIGHT(str, n) 更安全、意图更清晰。
-
LEFT('abcde', 3)返回'abc',等价于SUBSTRING('abcde', 1, 3),但省去了“从第 1 位开始”的冗余表达 -
RIGHT('abcde', 2)返回'de',而用SUBSTRING得写成SUBSTRING('abcde', -2)或SUBSTRING('abcde', LENGTH('abcde') - 1),后者还容易漏算长度 - 如果
n大于原字符串长度,LEFT/RIGHT直接返回原串;SUBSTRING(str, 1, n)同样安全,但SUBSTRING(str, pos, len)中若pos超出范围(比如SUBSTRING('a', 10)),结果为空字符串——这常被误认为是 bug
SUBSTRING 的 pos 参数为负数时到底怎么算
负数 pos 表示从末尾倒数,但注意:它是以 -1 对应最后一个字符,-2 对应倒数第二个,以此类推,不是“倒数第 n 个之后”。
-
SUBSTRING('hello', -2)→'lo'(从倒数第 2 位开始到结尾) -
SUBSTRING('hello', -2, 1)→'l'(从倒数第 2 位开始,取 1 个) -
SUBSTRING('hello', -6)→ 空字符串(因为总长才 5,-6 已越界) - 别和
SUBSTRING_INDEX混淆:SUBSTRING_INDEX('a.b.c', '.', -2)是'b.c',但那是按分隔符计数,和这里的负索引逻辑无关
想按分隔符切字符串?别硬套 SUBSTRING,用 SUBSTRING_INDEX
比如字段值是 'cn024_0415_[ln_dd]',要取下划线前的部分,有人会先用 LOCATE('_', str) 找位置,再套 SUBSTRING(str, 1, LOCATE('_', str) - 1)。这能跑通,但啰嗦且易错(LOCATE 找不到时返回 0,减 1 就变 -1,SUBSTRING 会出空)。
- 直接写
SUBSTRING_INDEX('cn024_0415_[ln_dd]', '_', 1),干净,找不到下划线时也原样返回整串 - 要取第一个下划线之后、第二个下划线之前?
SUBSTRING_INDEX(SUBSTRING_INDEX(str, '_', 2), '_', -1)—— 先取前两段,再取最后一段 - 注意分隔符是字符串,不是正则;想按多个字符切(如
'__'),它也能处理,但不会帮你做模糊匹配
性能和兼容性上几个实际坑点
这些函数在大多数 MySQL 版本(5.7+)行为一致,但仍有细节影响线上表现:
-
LEFT/RIGHT在某些旧版本(如 5.5)对TEXT字段可能隐式转成VARCHAR(255),导致截断;SUBSTRING更稳定 - 所有函数对
NULL输入都返回NULL,不是空字符串,做WHERE判断或拼接时得提前IFNULL或COALESCE - 如果字段含多字节字符(如中文、emoji),
LENGTH()返回字节数,CHAR_LENGTH()才是字符数;但LEFT/SUBSTRING默认按字符数截取,这点不用额外处理——除非你显式用了LENGTH做计算
真正麻烦的不是函数选哪个,而是混合使用时边界条件没兜住:比如用 LOCATE 定位再传给 SUBSTRING,却忘了检查定位失败的情况。这种地方加一层 IF 或换用 SUBSTRING_INDEX,往往比死磕 SUBSTRING 参数更省事。











