%匹配任意长度字符(含零),_仅匹配单字节字符,多字节字符场景易失效;需注意编码、显式escape声明及collation一致性。

LIKE语句中%和_通配符的实际行为差异
LIKE不是正则表达式引擎,%匹配任意长度(含零)的字符序列,_只匹配单个字符。很多人误以为_能跳过空白或忽略大小写——它不会,它只是占位一个字节位置,遇到多字节字符(如中文、emoji)时可能意外失效。
常见错误现象:SELECT * FROM users WHERE name LIKE 'A_' 本想查“A+一个字母”,结果把“A ”(A加空格)也捞出来;或者在UTF8MB4下匹配“阿_”,却漏掉“阿里”因为“里”占3字节,_只吃1字节。
- 用
_前先确认字段编码和字符宽度,对中文/emoji场景优先改用%或全文索引 - 需要严格单字符占位时,可加
COLLATE utf8mb4_bin避免大小写/重音干扰 - 若业务逻辑真依赖“第2位是X”,建议用
SUBSTRING(name, 2, 1) = 'X'更可控
ESCAPE子句解决通配符本身被当字面量的问题
当要查真实包含%或_的字符串(比如折扣码"50%OFF"),不加转义会全表扫描失败。此时必须用ESCAPE指定转义符,并在通配符前显式写出。
错误写法:WHERE code LIKE '%50%OFF%' → %被当通配符,结果错乱;正确写法:WHERE code LIKE '%50%OFF%' ESCAPE ''。注意:转义符本身不能是%、_或ESCAPE关键字,且不同数据库默认转义符不同(MySQL默认无,PostgreSQL默认无,SQL Server用!)。
- 统一用
ESCAPE显式声明,别依赖数据库默认值 - 转义符选
最通用,但需确认客户端/ORM是否自动转义反斜杠(Python里得写r'50%OFF'或'50\%OFF') - 若字段值来自用户输入,先用参数化方式拼接
ESCAPE字符串,避免SQL注入
LIKE查询性能差的根本原因与索引绕过陷阱
LIKE是否走索引,只看模式开头是不是固定字符串。例如name LIKE 'John%'能用B-tree索引,但name LIKE '%ohn%'或name LIKE '%John'一定全表扫描——因为B-tree按字典序存储,无法从结尾或中间定位。
有人试过加函数索引(如CREATE INDEX idx_name_rev ON users (REVERSE(name)))来支持后缀查找,但这只对LIKE '%John'有效,对'%John%'依然无效,且增加维护成本。
- 前导通配符(
%xxx)场景,考虑用全文索引(MATCH ... AGAINST)或外部搜索引擎(Elasticsearch) - 频繁做中间匹配(
%xxx%),检查是否真需要模糊查——有时业务上改成“标签系统”或预计算关键词列更高效 - MySQL 8.0+ 支持
REGEXP_LIKE,但性能通常比LIKE更差,别为了语法灵活牺牲可预期性
大小写敏感性由排序规则(collation)决定,不是LIKE本身
LIKE自己没有大小写开关,它完全服从字段的COLLATION。例如utf8mb4_general_ci是不区分大小写的,utf8mb4_bin是区分的。很多人在WHERE里写UPPER(name) LIKE UPPER('john%'),这不仅多余,还必然导致索引失效。
典型翻车点:开发库用_ci排序规则,测试通过;上线后生产库是_bin,突然查不到数据,还以为是缓存问题。
- 建表时明确指定collation,不要依赖数据库默认值
- 跨库迁移前用
SHOW CREATE TABLE核对collation是否一致 - 若需临时切换行为,用
COLLATE强制,如name LIKE 'John%' COLLATE utf8mb4_general_ci
实际项目里最常被忽略的是collation一致性与ESCAPE的显式声明——这两处出问题,往往要花半天排查,而修复只要两行SQL。










