mysql的SUBSTRING函数有三种等价写法:SUBSTRING(str, pos)、SUBSTRING(str FROM pos)、SUBSTRING(str, pos, len),位置pos从1开始;默认按字符截取,需确保字符集为utf8mb4;SUBSTR是其完全同义词;提取邮箱用户名等场景应优先使用SUBSTRING_INDEX而非组合LOCATE+SUBSTRING。

MySQL 的 SUBSTRING 函数怎么用?基本语法和参数顺序别搞反
SUBSTRING 在 MySQL 中有三种等价写法:SUBSTRING(str, pos)、SUBSTRING(str FROM pos)、SUBSTRING(str, pos, len)。注意:位置 pos 从 1 开始(不是 0),这点和多数编程语言不同,容易导致截取错位。
常见错误是把起始位置当成了 0 基索引,比如想取第 2 个字符却写了 SUBSTRING('abc', 0, 1),结果返回空字符串 —— 因为 MySQL 把负数或 0 当作无效起始点,部分版本甚至静默返回 NULL。
-
SUBSTRING('hello', 2)→'ello'(从第 2 个字符开始到末尾) -
SUBSTRING('hello', 2, 3)→'ell'(从第 2 个开始,取 3 个字符) -
SUBSTRING('hello' FROM 2 for 3)→ 等价于上一条,FOR是可选关键字
遇到中文或 UTF8 字符时,SUBSTRING 按字节还是按字符截?
MySQL 的 SUBSTRING 默认按「字符」截取,前提是列的字符集是 utf8mb4 且排序规则支持多字节字符(如 utf8mb4_unicode_ci)。但如果连接或会话的 character_set_client 设置不一致,可能触发隐式转换,导致中文被截成乱码或半个字符。
验证方式:执行 select Length('你好'), CHAR_LENGTH('你好');。若前者返回 6(UTF8MB4 下每个汉字占 3 字节)、后者返回 2,说明 CHAR_LENGTH 才是真实字符数,而 SUBSTRING 内部依赖的是 CHAR_LENGTH 逻辑 —— 所以它切的是字符,不是字节。
- 确保表字段用
utf8mb4,避免用过时的utf8(实际只支持最多 3 字节) - 避免在
WHERE条件中对大字段用SUBSTRING做模糊匹配,无法走索引 - 如果必须按字节截(极少见),得先转成二进制:
SUBSTRING(CAST(col AS BINARY), 5, 2)
SUBSTR 和 SUBSTRING 有什么区别?可以混用吗?
完全一样。SUBSTR 是 SUBSTRING 的同义词,MySQL 官方文档明确说明二者行为无任何差异,函数签名、参数含义、NULL 处理全部一致。代码里用哪个纯看团队风格或历史习惯。
但要注意:某些 ORM 或 SQL 检查工具(如 SonarQube 规则、SQLFluff)可能只认其中一个,或对别名做风格限制。线上环境混用不会报错,但统一用 SUBSTRING 更利于协作和审计。
- 以下两条语句等效:
SUBSTRING(name, 1, 4)和SUBSTR(name, 1, 4) - 不推荐写
SUBSTRING_INDEX时误敲成SUBSTR_INDEX—— 后者不存在,会直接报错function xxx.SUBSTR_INDEX does not exist - 如果迁移 oracle 或 postgresql 代码,注意它们的
SUBSTR虽然也从 1 开始,但部分旧版 Oracle 对负数位置的处理逻辑略有不同
从邮箱提取用户名(@前部分)这类场景,为什么别硬套 SUBSTRING?
单纯用 SUBSTRING 需要配合 LOCATE 计算位置,写法冗长且易出错:SUBSTRING(email, 1, LOCATE('@', email) - 1)。更麻烦的是,如果字段不含 @,LOCATE 返回 0,减 1 就变成 -1,此时 SUBSTRING 行为未定义(MySQL 5.7+ 返回空字符串,但早期版本可能报错或返回全串)。
真正健壮的做法是优先用 SUBSTRING_INDEX:SUBSTRING_INDEX(email, '@', 1)。它专为分隔符设计,天然处理缺失分隔符的情况(没 @ 就返回原字符串),无需额外判断。
-
SUBSTRING_INDEX('user@domain.com', '@', 1)→'user' -
SUBSTRING_INDEX('user@domain.com', '@', -1)→'domain.com'(负数表示从右往左数) - 嵌套使用也清晰:
SUBSTRING_INDEX(SUBSTRING_INDEX(url, '/', 3), '/', -1)提取协议后第一个路径段
真正难的不是记住函数名,而是意识到:字符串操作不是越“通用”越好,而是越贴近语义越稳。一个 SUBSTRING_INDEX 能解决的问题,硬拆成 LOCATE+SUBSTRING 只会增加边界风险。