LIKE的%和_本质是通配符,不是普通字符;SQL标准仅定义二者为通配符,数据库引擎默认按语义解释,需用方括号[](SQL Server)或ESCAPE(MySQL/PostgreSQL)显式转义才能匹配字面值。

LIKE 的 % 和 _ 本质是通配符,不是普通字符
SQL 标准里 LIKE 只定义了两个通配符:%(匹配任意长度字符串)和 _(匹配单个字符)。它们在模式匹配时被数据库引擎主动解释——只要出现,就按通配语义执行,不管数据里是不是真存着这个符号。
比如字段存了字符串 "report_2024",写 WHERE name LIKE '%_2024%',下划线会被当作“任意一个字符”来匹配,结果可能捞出 "reportA2024"、"data_2024" 甚至 "x_2024",完全不是你想要的“字面意义的下划线”。
这不是 bug,是设计使然:通配能力必须和字面匹配能力分离,否则模糊查询无法存在。
SQL Server 中方括号 [] 是最直接的字面量包装器
在 SQL Server 里,[] 不是“转义语法”,而是字符字面量声明机制。它对单个字符生效,且不依赖外部 ESCAPE 子句,因此更可靠、更少歧义。
-
LIKE '%[_]2024%'→ 精确匹配含下划线 + "2024" 的字符串 -
LIKE '%[%]2024%'→ 匹配含百分号 + "2024" -
LIKE '%[[]2024%'→ 匹配含左方括号[+ "2024"(注意第一个[开始字符集,第二个才是字面[) -
LIKE '%[]]2024%'→ 匹配含右方括号]+ "2024"(右括号必须紧贴开头,否则会被当成字符集闭合)
方括号不能嵌套,也不能用于多字符序列;想匹配 "[ab]" 这种字符串,得拆成 '%[[]a[b]%' ——先包左括号,再包字母 a 和 b,最后收尾,顺序错一个就失效。
MySQL / PostgreSQL 的 ESCAPE 用法差异大,容易踩坑
ESCAPE 不是万能钥匙,不同数据库对转义字符本身的处理逻辑完全不同:
- MySQL 5.7+ 默认认
为转义符,但字符串里要写两个反斜杠:LIKE '%_2024%' ESCAPE ''实际需写成'%\_2024%'(否则 SQL 解析器先吃掉一层) - PostgreSQL 完全不认
,必须显式指定单字符(如!),且该字符本身不能出现在待匹配文本中:LIKE '%!_2024%' ESCAPE '!' - SQL Server 虽支持
ESCAPE,但若转义符恰好是数据里的合法字符(比如用/转义,而路径字段里大量含/),就会误匹配或漏匹配
更隐蔽的问题:某些驱动或 ORM 在拼接 SQL 时会额外做一次字符串转义(比如 Python 的 % 格式化或 JS 模板字符串),导致反斜杠被吃掉两层,最终发给数据库的 pattern 已经变形。
真正难搞的是不可见字符和 Unicode 边界情况
当你要查的是 ASCII 0(CHAR(0))、BOM 头(U+FEFF)、零宽空格(U+200B)这类根本打不出来的东西,LIKE 和 ESCAPE 都无能为力——它们只工作在字符层面,不触达字节或码点。
这时候得换思路:
- 用
ASCII()或UNICODE()检查单个字符值:WHERE ASCII(SUBSTRING(content, 5, 1)) = 0 - 用
LEN()和DATALENGTH()对比长度差,判断是否有隐藏字节(如 UTF-8 BOM 导致多出 3 字节) - 正则更合适:
REGEXP '\x00'(MySQL 8.0+)、~ E'\u200b'(PostgreSQL),但注意正则元字符(.、*、+)和LIKE的转义体系互不兼容,别混用ESCAPE和双反斜杠
方括号和 ESCAPE 都只解决“看得见的特殊字符”,一旦字符不可见、编码不一致、或者跨数据库迁移,第一反应不该是调 ESCAPE 参数,而是先确认数据底层到底存了什么。

















