LIKE前置通配符(如'%abc')在存储过程中仍无法走索引,因B+树索引物理限制;优化关键是绕开该场景,而非调整SQL写法。

LIKE 前置通配符(如 '%abc')在存储过程中照样无法走索引——不是写法问题,是 B+ 树索引的物理限制;优化核心不是“怎么写 SQL”,而是“绕开索引不能用的场景”。
为什么存储过程里 LIKE '%abc' 依然全表扫描
存储过程只是把 SQL 封装起来执行,不改变查询优化器行为。只要 WHERE name LIKE '%abc' 这类模式出现,MySQL 仍无法利用 name 上的普通 B+ 树索引定位起点,只能扫全表或全索引叶子节点。
-
EXPLAIN中type显示ALL或index,且key为NULL,就确认没走索引 - 即使你在存储过程里用
CONCAT('%', @keyword)拼接,也一样失效——表达式包裹列会阻断索引下推 - 参数化传入不影响判断:
WHERE name LIKE in_keyword,若in_keyword值是'%abc',照样退化
能真正提速的三种实操路径
别在存储过程里硬扛 LIKE '%abc',优先选下面任一方案,再封装进存储过程:
-
反向虚拟列 + 索引(MySQL ≥ 5.7.6):添加
GENERATED ALWAYS AS (REVERSE(name)) STORED列,建索引,查询改写为WHERE v_name LIKE CONCAT(REVERSE(@keyword), '%') -
FULLTEXT + ngram(中文必备):对
name建FULLTEXT(name),存储过程中用MATCH(name) AGAINST(@keyword IN NATURAL LANGUAGE MODE);注意@keyword不带%,且需提前开启ngram_token_size=2 -
UNION 覆盖前缀+后缀:当业务允许拆查(比如用户搜“张三”,同时支持“张三%”和“%张三”),用
SELECT ... WHERE name LIKE CONCAT(@keyword, '%') UNION SELECT ... WHERE REVERSE(name) LIKE CONCAT(REVERSE(@keyword), '%'),两个分支都可走索引
存储过程里写 LIKE 必须避开的坑
哪怕你用了正确模式,存储过程中几个细节照样让索引失效:
- 别在字段上套函数:
UPPER(name) LIKE UPPER(@keyword)→ 改成统一存小写 + 直接name LIKE @keyword - 别漏转义:
@keyword若含_或%字面量(如查文件名report_2024),必须先处理:SET @safe_key = REPLACE(REPLACE(@keyword, '_', '\_'), '%', '\%');,再配合ESCAPE '\' - 别用
CONCAT('%', @keyword, '%')当条件——它等价于LIKE '%...%',永远扫表;真要中间模糊,应评估是否该换全文索引 - 如果
@keyword来自外部输入,务必在调用存储过程前 trim 空格,否则' 张三 '导致索引选择性骤降
虚拟列和 FULLTEXT 都需要 DDL 权限,上线前得确认账号权限;ngram 对单字检索无效,比如搜“我”“你”根本不出结果——这点最容易被忽略,测试时得拿真实语料过一遍。

















