MySQL的SUBSTRING()函数位置索引从1开始,pos=0返回空字符串;len必须为非负整数,否则返回空;负pos从右计数;需配合LOCATE/POSITION时应先判断分隔符存在性,或改用SUBSTRING_INDEX()等更安全的函数。

起始位置从1开始,不是0
MySQL的SUBSTRING()函数位置索引是**1-based**,这点和多数编程语言(如Python、JavaScript)的0-based截然不同。传入pos = 0会返回空字符串,pos = 1才是第一个字符。容易踩的坑是习惯性写SUBSTRING(str, 0, 3)想取前三位,结果啥也得不到。
常见错误现象:SUBSTRING('abc', 0, 2) → 返回空字符串(不是'ab');SUBSTRING('abc', 1, 2) → 正确返回'ab'。
- 正数位置:从左往右数,第1个字符是起始点
- 负数位置:从右往左数,
-1是最后一个字符,-2是倒数第二个 - 若
pos超出字符串长度(比如对5字符字符串用pos = 10),返回空字符串,不会报错
len参数为负值时无效,且不报错
SUBSTRING(str, pos, len)中的len必须是非负整数。传入负值(如-3)会导致整个函数返回空字符串,且MySQL不会抛出警告或错误——这会让调试变得隐蔽。
实际场景中,如果逻辑里误把长度计算成负数(例如用LOCATE('-', str) - 1但LOCATE返回0时),结果就是全字段变空,而你可能还在查数据源有没有问题。
-
SUBSTRING('hello', 2, -1)→''(空字符串) -
SUBSTRING('hello', 2, 0)→''(合法但截不出内容) -
SUBSTRING('hello', 2, 3)→'ell'(正常) - 安全做法:在动态计算
len前加GREATEST(0, ...)兜底,例如SUBSTRING(str, start, GREATEST(0, end - start))
结合POSITION()或LOCATE()提取分隔符后内容时要注意边界
想从邮箱user@domain.com中提取域名,常写SUBSTRING(email, POSITION('@' IN email) + 1)。但若某条记录没有@,POSITION()返回0,+1后变成1,结果就变成截取整个字符串——这不是你想要的“无@则为空”,而是静默错误。
更稳妥的做法是先判断分隔符是否存在:
- 用
CASE WHEN LOCATE('@', email) > 0 THEN SUBSTRING(email, LOCATE('@', email) + 1) ELSE NULL END - 或用
SUBSTRING_INDEX(email, '@', -1)(更简洁,但语义不同:它按分隔符切分,取最后一段,对a@b@c会返回c,而SUBSTRING + LOCATE只认第一个@) -
LOCATE()比POSITION()多一个可选起始参数,适合跳过开头的干扰字符
SUBSTRING和SUBSTRING_INDEX适用场景不能混用
SUBSTRING()是**基于位置+长度**的硬截取,SUBSTRING_INDEX()是**基于分隔符计数**的逻辑切分。两者目标不同,强行替换会出错。
比如处理路径/home/user/docs/report.pdf:
- 要取最后一级文件名:
SUBSTRING_INDEX(path, '/', -1)→'report.pdf'(推荐) - 若用
SUBSTRING(),得先LOCATE('/', path, LENGTH(path)-10)反向找最后一个/,复杂且易错 - 要取前两级路径:
SUBSTRING_INDEX(path, '/', 3)→'/home/user';用SUBSTRING()就得嵌套多次LOCATE(),维护成本高 - 当分隔符不存在时,
SUBSTRING_INDEX()直接返回原字符串;SUBSTRING()则取决于你给的位置是否有效
真正需要精准控制字节偏移(比如固定格式日志中第12–18位永远是订单类型)时,才非用SUBSTRING()不可;其余多数业务场景,优先考虑SUBSTRING_INDEX()、LEFT()、RIGHT()等语义更清晰的函数。


















