优先用LIKE,因其跨数据库兼容;仅当需定位位置时才选INSTR/CHARINDEX等函数,且注意各库语法差异与索引限制。

用 LIKE 还是 INSTR?先看数据库类型
MySQL 和 Oracle 支持 INSTR(),返回位置索引,适合需要判断“是否在开头/中间/结尾”或后续截取的场景;PostgreSQL 用 POSITION() 或 STRPOS();SQL Server 没有原生 INSTR(),得用 CHARINDEX()。但如果你只关心“包不包含”,LIKE 在所有主流数据库里都可用,写法统一:WHERE column_name LIKE '%keyword%'。
常见错误是忽略大小写:MySQL 默认不区分,但 PostgreSQL 和 SQL Server 默认区分,得加 ILIKE 或 LOWER() 包裹:
WHERE LOWER(description) LIKE '%error%'
性能上,LIKE 带前后通配符('%keyword%')无法走索引,全表扫描不可避免;而 INSTR(col, 'key') > 0 同样无法利用普通 B-tree 索引——除非你建了函数索引(如 PostgreSQL 的 CREATE INDEX ON t ((lower(col))))或 MySQL 8.0+ 的隐藏索引。
避免误匹配:逗号分隔字段里的 ID 查找
字段存的是 '1,3,5,13,23',你想查“含 3”,但 LIKE '%3%' 会把 13、23 全捞出来。这不是语法问题,是语义错配。
正确做法取决于数据规范程度:
- 如果字段结构固定且允许改表,优先转成关联表(
user_id+tag_id),这是最干净的解法 - 如果必须查字符串,MySQL 可用
FIND_IN_SET('3', tag_list);PostgreSQL 用',' || tag_list || ',' LIKE '%,3,%';SQL Server 得拼',' + tag_list + ',' LIKE '%,3,%' - Oracle 可用
',' || tag_list || ',' LIKE '%,3,%',或正则REGEXP_LIKE(tag_list, '(^|,)3(,|$)')
注意:这些写法全都无法走索引,大数据量下务必加覆盖条件(比如限定时间范围、状态字段)来缩小扫描集。
想定位关键字位置?别只用布尔判断
仅用 LIKE 或 INSTR() > 0 只能回答“有没有”,但业务常需要“在哪”——比如日志字段里找 'ERROR:' 后面的内容,或提取 URL 中的域名。
这时 INSTR()(MySQL/Oracle)或 CHARINDEX()(SQL Server)就派上用场了:
SELECT SUBSTRING(log_text, INSTR(log_text, 'ERROR:') + 6, 100) FROM logs WHERE INSTR(log_text, 'ERROR:') > 0;
关键点:
-
INSTR()返回的是从 1 开始的位置,不是 0;没找到时返回 0,直接参与SUBSTRING会出错,所以 WHERE 条件不能省 - MySQL 8.0+ 支持
REGEXP_SUBSTR(),比嵌套SUBSTRING + INSTR更稳,但性能略低 - SQL Server 的
CHARINDEX()第二个参数是待查字符串,顺序和INSTR()相反,别写反
查得慢?先看执行计划,再想优化
只要查询变慢,第一反应不该是换函数,而是跑 EXPLAIN(MySQL)或 EXPLAIN ANALYZE(PostgreSQL)。重点盯两个字段:
-
type是ALL还是index?说明在全扫表或全扫索引——基本等于没索引可用 -
rows数值是否远超实际返回行数?比如查 10 行却要读 50 万行,大概率是条件没走索引或索引失效
临时提速技巧有限:给字段加前导索引(LIKE 'prefix%' 可走索引)、用全文索引(MATCH ... AGAINST)、或者把高频检索字段单独抽成生成列并建索引(MySQL 5.7+)。但最常被忽略的一点是:很多“包含查询”其实可以前置过滤——比如先用 created_at > '2026-09-01' 缩小数据集,再在结果里做文本匹配。










