MySQL中SUBSTRING有SUBSTRING/SUBSTR/MID三种等价写法,关键遵循“起始位置从1开始”和“长度可省略”两条规则;中文截取需注意utf8mb4下按字节而非字符,易致乱码,应配合CHAR_LENGTH使用。

SUBSTRING 函数的三种调用形式怎么选
SUBSTRING 在 MySQL 里其实有三个等价写法:SUBSTRING()、SUBSTR()、MID(),它们行为完全一致,选哪个纯看团队习惯或可读性。但参数组合容易混淆——关键不是记名字,而是分清「起始位置从 1 开始」和「长度可省略」这两条铁律。
常见错误是照搬其他语言(比如 Python 的切片)以为下标从 0 开始,结果 SUBSTRING('abc', 0, 1) 返回空字符串,而 SUBSTRING('abc', 1, 1) 才得到 'a'。
- 最常用形式:
SUBSTRING(str, pos, len)—— 指定起始位置和截取长度 - 省略长度:
SUBSTRING(str, pos)—— 从 pos 一直截到末尾 - 支持负数 pos:
SUBSTRING('hello', -2)返回'lo'(从倒数第 2 位开始),但不支持负数 len
中文字符截取为什么经常出错
MySQL 默认按字节截取,不是按字符。如果字段是 utf8mb4 编码(推荐),一个中文占 3–4 字节,SUBSTRING(name, 1, 2) 可能只拿到半个汉字,显示成乱码或问号,尤其在日志或前端展示时突然崩掉。
这不是函数 bug,是编码与截取单位不匹配。解决办法只有一个:确保你清楚字段实际编码,并优先用字符长度函数辅助判断:
- 用
CHAR_LENGTH(str)获取字符数,别用LENGTH(str)(那是字节数) - 如果必须按“前 N 个汉字”截取,先用
CHAR_LENGTH()校验长度是否足够,再调SUBSTRING() - 避免在 WHERE 条件里对大字段频繁用
SUBSTRING()做模糊匹配——会跳过索引,查得慢
在 ORDER BY 或 GROUP BY 里用 SUBSTRING 容易踩什么坑
直接写 ORDER BY SUBSTRING(title, 1, 10) 看似没问题,但 MySQL 会为每一行实时计算,无法利用 title 字段上的索引,数据量一过万就明显变慢。
更隐蔽的问题是排序结果不稳定:如果两个字符串前 10 个字符相同,MySQL 会按完整值再比较,但你没显式声明,可能误以为只比了子串。
- 想按前缀排序且要性能?建生成列 + 索引:
ALTER TABLE posts ADD COLUMN title_prefix VARCHAR(10) STORED AS (SUBSTRING(title, 1, 10)); CREATE INDEX idx_title_prefix ON posts(title_prefix); - GROUP BY 场景同理,别在分组字段里嵌套
SUBSTRING(),先算好存起来再分组 - 注意 NULL 处理:
SUBSTRING(NULL, 1, 5)返回NULL,参与排序或分组时会被单独归为一组
替代方案:什么时候不该硬用 SUBSTRING
想提取域名、手机号区号、日期中的年份?硬套 SUBSTRING() 很快会失控。它只是“从哪切几刀”,不理解语义。
真正该上正则或专用函数的时候到了——尤其是 MySQL 8.0+ 支持 REGEXP_SUBSTR(),比拼接多个 SUBSTRING() 加 LOCATE() 清晰太多。
- 提取邮箱 @ 后的域名:
REGEXP_SUBSTR(email, '@([^@]+)$', 1, 1, '', 1),比SUBSTRING(email, LOCATE('@', email) + 1)更健壮 - 想按分隔符拆字符串?
SUBSTRING_INDEX()是专门为此设计的,比如SUBSTRING_INDEX('a,b,c', ',', 2)返回'a,b' - 做数据清洗时,如果源格式多变,优先考虑应用层处理,数据库里留简单、确定的逻辑
字符串操作看着简单,但一旦混入编码、性能、可维护性,就很容易在某个凌晨三点因为一个错位的下标被叫醒。留心 pos 起始值、检查字段编码、别在关键路径上现场计算子串——这些点比记住语法重要得多。











