%匹配任意长度(含零)字符,_仅匹配单个字符;前者易致结果泛滥且前置时索引失效,后者强制长度约束,查字面%或_须用ESCAPE转义。

LIKE语句中%和_通配符的实际行为差异
%匹配任意长度(包括零长度)的字符序列,_只匹配单个任意字符。很多人误以为_能跨字节匹配中文,但实际在多数数据库(如MySQL默认utf8mb4)里,它仍只占一个字符位置——也就是说,一个汉字算一个字符,_确实能匹配一个汉字,但不能跳过或替代两个字。
常见错误是写WHERE name LIKE '_明'想匹配“小明”“大明”,结果漏掉“阿明”(因为前面是两个字),其实该用'%明';反过来,若要严格找“X明”这种两字名,才用'_明'。
- MySQL 8.0+ 支持
ESCAPE自定义转义符,比如LIKE '100\%' ESCAPE '\'可查字面量“100%” - PostgreSQL 对大小写敏感,
ILIKE才是不区分大小写的等价写法 - SQL Server 中
_在某些排序规则下可能受Unicode组合字符影响,建议测试真实数据
中文模糊搜索时LIKE性能骤降的根源
LIKE查询一旦以%开头(如LIKE '%搜索词'),数据库基本无法使用索引,全表扫描不可避免。这不是写法问题,是B+树索引结构决定的——前导通配符让“从左到右”的有序查找失效。
实测:100万行用户表,WHERE nickname LIKE '张%'可能走索引,耗时20ms;换成'%张%'后升至1.2秒以上,且CPU飙升。
- 替代方案优先考虑全文索引(MySQL
FULLTEXT、PostgreSQLtsvector) - 若必须用LIKE,把固定前缀尽量往前放,例如
'张%丰'比'%张%丰%'更容易触发部分索引利用 - 避免在WHERE中对字段做函数处理,如
LOWER(name) LIKE '%abc%'会彻底禁用索引
不同数据库对特殊字符和空格的LIKE处理差异
空格是否被匹配、反斜杠是否默认转义、方括号[]是否支持字符集——这些都因数据库而异。MySQL默认不识别[a-z],PostgreSQL默认也不支持,只有SQL Server原生支持LIKE '[A-Z]ohn'这类写法。
更隐蔽的问题是尾部空格:SQL标准规定CHAR类型字段会补空格,所以'abc '和'abc'在LIKE比较时可能意外相等(尤其在SQL Server中)。而VARCHAR通常不会。
- MySQL中
LIKE默认不区分末尾空格,可用COLLATE utf8mb4_bin强制二进制比较 - PostgreSQL需显式写
name::bytea LIKE '%\x00%'才能匹配ASCII 0,普通%不匹配控制字符 - 所有数据库中,直接写
LIKE '%\%%'会报错,必须用ESCAPE或双写%%(取决于驱动解析逻辑)
参数化查询中拼接LIKE值的正确姿势
应用层传参时,如果把%硬编码进SQL字符串(如"WHERE name LIKE '%"+input+"%'"),不仅有SQL注入风险,还会让数据库无法复用执行计划。预编译参数本身不支持通配符自动包裹,得由代码处理。
正确做法是在应用代码里拼好带通配符的值,再作为完整参数传入:PreparedStatement.setString(1, "%" + userInput + "%")。这样既安全,又能让数据库缓存同一模板的执行计划。
- MyBatis中用
#{}接收参数,通配符写在Java层或用<bind>标签生成 - 不要用
${}拼SQL,那是把用户输入直接塞进SQL文本 - 如果前端传的是“张*”,后端需识别
*并转成%,注意别把\*也转了
LIKE看着简单,但每个数据库对空格、排序规则、索引策略的实现细节都不一样。上线前务必用真实数据量+真实字符集跑一遍执行计划,光看语法对不对没用。

















