mysql如何使用substring提取子串_mysql字符串截取方法

1次阅读

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

mysql如何使用substring提取子串_mysql字符串截取方法

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)

SUBSTRSUBSTRING 有什么区别?可以混用吗?

完全一样。SUBSTRSUBSTRING 的同义词,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
  • 如果迁移 oraclepostgresql 代码,注意它们的 SUBSTR 虽然也从 1 开始,但部分旧版 Oracle 对负数位置的处理逻辑略有不同

邮箱提取用户名(@前部分)这类场景,为什么别硬套 SUBSTRING

单纯用 SUBSTRING 需要配合 LOCATE 计算位置,写法冗长且易出错:SUBSTRING(email, 1, LOCATE('@', email) - 1)。更麻烦的是,如果字段不含 @LOCATE 返回 0,减 1 就变成 -1,此时 SUBSTRING 行为未定义(MySQL 5.7+ 返回空字符串,但早期版本可能报错或返回全串)。

真正健壮的做法是优先用 SUBSTRING_INDEXSUBSTRING_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 只会增加边界风险。

text=ZqhQzanResources