能截,但需instr定位起始位置、substring按长度截取,二者必须配合使用;instr只返回首次出现的下标(从1开始),未找到返回0,直接传入substring会导致错误或空结果。

MySQL里用SUBSTRING和INSTR截取固定分隔符之间的内容
直接说结论:能截,但得配对用,INSTR找位置,SUBSTRING切长度,漏掉一个参数就返回空或错位。
常见错误是把INSTR当“查找并返回子串”用,其实它只返回起始下标(从1开始),而且不支持正则、不支持从右往左搜。比如想从'user@example.com'里取'example',得先定位'@'和'.'的位置,再算中间长度。
-
INSTR(str, substr)返回第一次出现的位置,没找到返回0 —— 这个0后续传给SUBSTRING会导致截取失败,必须判断 -
SUBSTRING(str, pos, len)第三个参数len可省略,但省略后会截到末尾;如果pos超出长度,结果为空字符串 - 注意:MySQL中索引从1开始,不是0,
SUBSTRING('abc', 2, 1)得到'b',不是'a'
示例:从'order_20240512_8891.json'中提取订单号'8891':
SELECT SUBSTRING(
'order_20240512_8891.json',
INSTR('order_20240512_8891.json', '_') + 1,
INSTR('order_20240512_8891.json', '.json') - INSTR('order_20240512_8891.json', '_') - 1
);
PostgreSQL里没有INSTR,得换POSITION或STRPOS
PostgreSQL不认INSTR,直接报错function instr(unknown, unknown) does not exist。必须换成POSITION(标准SQL)或STRPOS(PostgreSQL特有),二者行为一致,都返回从1开始的偏移量。
但POSITION语法是POSITION('substr' IN str),顺序和INSTR相反,容易写反;STRPOS更接近MySQL习惯,推荐用它。
-
POSITION('_' IN 'a_b_c')→ 返回2;STRPOS('a_b_c', '_')→ 同样返回2 - 如果分隔符可能不存在,
POSITION返回0,STRPOS也返回0,后续计算前必须WHERE STRPOS(...) > 0过滤,否则SUBSTRING会截出意外内容 - PostgreSQL的
SUBSTRING支持正则变体:SUBSTRING(str FROM pattern),对复杂模式更稳,但性能略低
等效示例(取邮箱用户名):
SELECT SUBSTRING('user@example.com' FROM 1 FOR STRPOS('user@example.com', '@') - 1);
遇到嵌套分隔符或多个相同分隔符时,INSTR默认只找第一个
比如'a.b.c.d'想取第二个点之后的内容(即'c.d'),INSTR(str, '.')+1只能定位第一个点,没法跳过前N次匹配。
MySQL 8.0+ 可用REGEXP_SUBSTR替代,但老版本只能靠嵌套INSTR:用第二次INSTR从第一次位置+1之后再搜。
- 找第2个
'_'的位置:INSTR(str, '_', INSTR(str, '_') + 1) - 找第3个:再套一层,
INSTR(str, '_', INSTR(str, '_', INSTR(str, '_') + 1) + 1) - 这种写法极易出错,尤其当某次
INSTR返回0时,整个表达式变成INSTR(..., ..., 0 + 1)→ 从位置1重搜,逻辑就乱了
稳妥做法是加CASE WHEN逐层判断是否存在:
SELECT CASE WHEN INSTR(str, '_') > 0 AND INSTR(str, '_', INSTR(str, '_') + 1) > 0 THEN SUBSTRING(str, INSTR(str, '_', INSTR(str, '_') + 1) + 1) ELSE NULL END
跨数据库移植时,函数名、参数顺序、空值处理全都不一样
SQL标准里根本没有INSTR,它是MySQL/Oracle的私货;SQL Server用CHARINDEX;SQLite用INSTR但参数顺序和MySQL一致;而PostgreSQL彻底不用它。
更麻烦的是空值传播规则:MySQL中INSTR(NULL, 'x')返回NULL,SUBSTRING(NULL, 1, 5)也返回NULL;但SQL Server里CHARINDEX('x', NULL)直接报错,必须ISNULL兜底。
- 写可移植SQL时,优先考虑
POSITION(标准SQL)+SUBSTRING(标准SQL),兼容性最好 - 如果必须用
INSTR,别在WHERE里直接用它做条件判断,先用COALESCE(INSTR(...), 0) > 0防NULL穿透 - 所有涉及位置计算的表达式,都要预留“找不到分隔符”的分支,不能假设数据格式永远规范
最常被忽略的一点:不同数据库对多字节字符(如中文、emoji)的INSTR/POSITION计数方式可能不同,MySQL按字符,某些配置下按字节,一旦字段是utf8mb4又没设好collation,位置就偏了。










