instr在oracle中替代like '%keyword%'可提升性能近10倍,因其单次扫描定位优于like逐字符回溯;但不走索引,仅比全表扫描的like更轻量,真正索引优化需前缀匹配或函数索引。

Oracle 用 INSTR 替代 LIKE '%keyword%'
直接结论:INSTR 在 Oracle 中对中间匹配('%keyword%')的性能明显优于 LIKE,尤其在千万级表上实测快近 10 倍。它不依赖索引,但只做一次扫描定位,避免了 LIKE 对每个字符逐位回溯匹配的开销。
常见错误是以为 INSTR 能走索引——它不能,但它比全表扫 LIKE 更轻量。真正要走索引,必须用前缀匹配(LIKE 'keyword%')或函数索引(如 CREATE INDEX idx_title_upper ON t(UPPER(title)))。
使用要点:
-
INSTR(title, '手册') > 0等价于title LIKE '%手册%',但执行更快 - 起始位置和出现次数参数慎用:省略时默认从头找第一次;传负数会从右往左查,但多数模糊场景不需要
- 返回 0 表示未找到,注意 NULL 字段参与时结果仍为 0(不是 NULL),逻辑上等同于
NOT LIKE
MySQL 优先选 LOCATE 或 INSTR,别用 FIND_IN_SET
LOCATE('kw', col) 和 INSTR(col, 'kw') 功能一致(后者是前者的别名),都返回子串首次出现位置,0 表示不存在。它们比 LIKE '%kw%' 略快,因为跳过通配符解析开销,且 MySQL 优化器对这类函数更友好。
FIND_IN_SET 完全不是为通用模糊查询设计的:它要求字段值是逗号分隔的枚举字符串(如 'a,b,c'),且只匹配完整项。拿它查含子串的普通文本,结果一定为空或误判。
关键区别:
-
LOCATE('359950439_', sys_pid) > 0→ 正确,查子串存在性 -
FIND_IN_SET('359950439_', sys_pid)→ 错误,除非sys_pid是类似'123,359950439_,456'的格式 - 所有函数都不支持索引加速中间匹配,但
LOCATE/INSTR的 CPU 开销更低
SQL Server 用 CHARINDEX,但得避开 LIKE 开头带 % 的写法
CHARINDEX('kw', col) > 0 和 col LIKE '%kw%' 语义相同,但前者在执行计划中更容易被优化器识别为“简单存在判断”,尤其当字段有非空约束、统计信息较新时,有时能触发更紧凑的扫描路径。
真正卡性能的是 LIKE 自身的通配符位置:LIKE '%kw'(后缀匹配)和 LIKE '%kw%'(中间匹配)必然全表扫描;只有 LIKE 'kw%' 才可能走索引——前提是索引存在、选择性够高,且没被隐式转换干扰(比如字段是 VARCHAR,而参数是 NVARCHAR)。
实操提醒:
- 用
EXPLAIN(或 SQL Server 的执行计划)确认type是index或range,而不是ALL - 避免写成
WHERE CHARINDEX(@kw, col) > 0后又在应用层拼@kw = '%' + @input + '%'——这会让函数失效,退化成LIKE语义 - 如果必须查后缀,MySQL 8.0+ 可建反向索引:
CREATE INDEX idx_name_rev ON t((REVERSE(name))),再查REVERSE(name) LIKE REVERSE('abc') + '%'
跨数据库统一写法难,但可规避最大陷阱
没有哪个函数能在所有数据库里既语法一致又性能等效。INSTR 在 Oracle/MySQL 都可用,但 PostgreSQL 不支持;POSITION 是 SQL 标准函数,MySQL/PostgreSQL 支持,SQL Server 不认;CHARINDEX 是 SQL Server 专属。
最该守住的底线是:别让模糊条件变成全表扫描的开关。百万级以上数据,LIKE '%kw%' 就是性能红灯,不管换什么函数,只要逻辑还是“任意位置包含”,就只是把慢从 5 秒压到 3 秒,而非解决根本问题。
容易被忽略的点:
- 中文字段要核对排序规则(
COLLATION),utf8mb4_0900_as_cs和utf8mb4_unicode_ci对“张/張”匹配结果不同 - 搜真实含
%或_的内容,必须用ESCAPE,比如WHERE comment LIKE '%10!%' ESCAPE '!' - 真正需要“含关键词”语义的场景,该上全文索引(
MATCH AGAINST、tsvector)或 Elasticsearch,而不是在LIKE和字符串函数之间反复调优











