很多开发者第一次接触 LIKE,是在写“模糊查询”的时候:输入一个关键词,把包含它的记录全部捞出来。这个需求太常见了,常见到我们几乎不会停下来想一个问题——LIKE 真的是实现模糊匹配的最好方案吗?它有哪些容易踩的坑?为什么有时候一条 LIKE 查询能把数据库拖慢好几个数量级?为什么同一个 LIKE 在 MySQL 和 SQL Server 里表现还不一样?这些问题,才是这篇文章真正想讲清楚的东西。
不是简单罗列 LIKE 的语法,而是把 LIKE 放在真实的数据库管理系统场景里,从基础用法、通配符语义、大小写规则、ESCAPE 转义,一路讲到性能优化、SQL 注入风险和替代方案。无论你刚学 SQL,还是在生产环境里被慢查询折磨过,这篇文章都值得读完并收藏。
1. 这篇文章真正要解决的问题
先把话说在前面:SQL 里的 LIKE 远不止“加两个百分号”那么简单。很多初学者以为 LIKE 就是WHERE name LIKE '%关键词%',写完能出结果就算会了。但实际工作中,围绕 LIKE 的问题几乎都是下面这几种:
- 查出来的结果不对:明明数据存在,LIKE 却匹配不到,或者匹配到了不该匹配的,比如大小写、空格、通配符被当成普通字符。
- 查询慢到无法接受:一张几百万行的表,
LIKE '%关键词%'把全表扫了一遍,接口直接超时。 - 拼接 SQL 导致安全风险:用户输入被直接拼进 LIKE 条件,等于给 SQL 注入开了门。
- 不同数据库行为不一致:同样的写法在 MySQL 里没问题,换到 SQL Server 或 PostgreSQL 就踩坑。
这篇文章会对上述问题逐一拆解。你不是来这里背语法的,你是来弄清楚 LIKE 怎么用才不出错的。读完以后,你应该能回答三个问题:什么场景该用 LIKE?用的时候要注意什么?当 LIKE 不够用时,有什么更好的替代方案?
2. LIKE 的基础概念与适用场景
2.1 什么是 LIKE
LIKE 是 SQL 标准中用于字符串模式匹配的操作符,通常放在WHERE子句里,配合通配符实现“模糊匹配”。它的核心作用可以概括为一句话:判断某个字段的值是否符合指定的模式。
与之对应的=是精确匹配。举个例子:
WHERE name = '张三'只返回姓名恰好是“张三”的记录。WHERE name LIKE '张%'返回所有姓“张”的人。
这两者的差别,就是程序世界里“等值判断”和“模式判断”的差别。=关心的是“你是不是这个值”,LIKE 关心的是“你符不符合我描述的形态”。
2.2 LIKE 解决什么问题
在真实业务里,用户很少能给出完整准确的值。搜索框里输入“华为”,可能想找的是“华为Mate 60 Pro”“华为笔记本”“华为云服务器”。这种前缀匹配(prefix matching)用=根本写不出来,而 LIKE 可以。
再比如手机号中间四位做脱敏查询、订单号模糊搜索、日志表里按关键字过滤消息,这些都是 LIKE 的典型应用场景。它解决的是数据库管理系统中“不确定完整值”的匹配问题,是精确匹配=的有力补充。
2.3 什么样的场景最适合 LIKE
从实际工程经验来看,LIKE 最适合下面几类场景:
- 数据量可控的模糊搜索。比如一张配置表、用户表,量级在几十万以内,LIKE 前缀匹配配合索引可以接受。
- 需要兼容多种数据库的简单模糊查询。LIKE 是标准 SQL 能力,MySQL、Oracle、SQL Server、PostgreSQL 全都支持。
- 模式匹配逻辑简单,不需要正则表达式那么强的表达能力。
- 作为全文检索的降级方案。数据量没到那个量级,没必要引入 Elasticsearch,LIKE 够用就先凑合。
但也要泼一盆冷水:LIKE 并不适合所有模糊搜索场景,尤其是你对查询性能和匹配精度都有要求的时候。后面第五、第六部分会专门讲这个问题。
3. LIKE 语法与通配符详解
3.1 LIKE 基本语法
LIKE 的语法非常简单,标准的 SQL 写法是:
SELECT 列名 FROM 表名 WHERE 列名 LIKE 模式;模式(pattern)是一个字符串,里面可以包含通配符。SQL 标准里有两个核心通配符:%和_。
3.2 通配符 % 的含义与用法
%表示匹配任意长度(包括 0)的任意字符序列。
| 模式 | 含义 | 匹配示例 |
|---|---|---|
张% | 以“张”开头 | 张三、张伟、张无忌 |
%张 | 以“张”结尾 | 老张(如果姓在后)、组合张 |
%张% | 包含“张” | 张三、老张头、小张三 |
张%三 | 以“张”开头,以“三”结尾 | 张三、张飞三(但要求同一字段连续满足) |
注意一个细节:'张%'会匹配“张”本身,因为%可以代表 0 个字符。这在很多面试题里出现过,属于基础但容易忽略的点。
3.3 通配符 _ 的含义与用法
_表示匹配单个任意字符。它和%的区别,关键在于“量”的精确限制。
| 模式 | 含义 | 匹配示例 |
|---|---|---|
张_ | “张”后面恰好一个字符 | 张三、张四,不匹配“张伟强” |
张__ | “张”后面恰好两个字符 | 张伟强、张小美,不匹配“张三” |
_张% | 第二个字符是“张” | 老张、小张,不匹配“张伟” |
实际使用中,_的使用频率远低于%,因为业务上“精确控制一个字符长度”的需求不算多。但在字符串格式校验类场景里,_很有用,比如校验手机号前三位和后四位。
理解%和_的最佳方式,是把它们类比为正则表达式中的.*和.。虽然语义不完全等价(正则里.*可以匹配换行符之外任意字符,SQL 里%匹配的字符集跟排序规则有关),但思维模型是一样的:一个是“任意长度”,一个是“单个位置”。
3.4 不同数据库中的 LIKE 通配符差异
%和_在 SQL 标准里是通用的,这点必须记住。但不同数据库管理系统在细节上有差异:
- MySQL:默认情况下
LIKE不区分大小写(取决于表的排序规则,通常为utf8_general_ci或utf8mb4_general_ci,_ci 结尾就是不区分大小写)。另外 MySQL 还支持非标准的ESCAPE子句来指定转义字符。 - PostgreSQL:
LIKE区分大小写,但提供了ILIKE做不区分大小写的匹配。还支持SIMILAR TO和正则表达式函数。 - SQL Server:
LIKE支持标准通配符,还额外支持[a-z]字符集匹配和[^a-z]反向匹配。这是非常实用的增强能力。 - Oracle:
LIKE区分大小写,可以使用UPPER(列名) LIKE UPPER('%关键词%')来做不区分大小写的查询。
这段内容先留个印象,后面第四部分会通过代码示例体现这些差异。
3.5 ESCAPE 子句:如何匹配通配符本身
如果用户搜索的关键词里恰好包含%或_,怎么处理?比如要查名字叫“100%纯棉”的商品,LIKE '%100%%%'就会让数据库糊涂。
答案是用ESCAPE子句指定一个转义字符。默认的转义字符可以不用写,但需要显式声明:
-- 查询商品名称中包含“100%”的记录 SELECT * FROM products WHERE product_name LIKE '%100\%%' ESCAPE '\';这段 SQL 的含义是:\后面的%是普通字符,而不是通配符。第一个%和最后一个%仍然是通配符。
这个细节在实际项目中很容易踩坑。用户输入搜索关键词时,程序必须对用户输入里的%、_、\做转义处理,否则用户搜“100%棉”会返回一堆奇怪的数据。
下面给一个 Java 后端转义 LIKE 关键字的参考实现:
public static String escapeLike(String input) { if (input == null) { return null; } return input .replace("\\", "\\\\") .replace("%", "\\%") .replace("_", "\\_"); }然后在拼接 SQL 时使用ESCAPE '\'。这块如果不处理,开发环境里测试数据少问题不明显,一到生产环境用户乱输入就开始出问题了。
4. LIKE 完整示例与代码实现
4.1 环境准备
为了让示例真实可跑,本文以 MySQL 8.0 作为主要演示环境,同时标注 SQL Server 和 PostgreSQL 的差异点。一个简单的学生表 + 成绩表就够用了。如果你本地没有 MySQL,可以用 Docker 快速起一个:
docker run --name mysql-like-demo \ -e MYSQL_ROOT_PASSWORD=root123 \ -e MYSQL_DATABASE=student_db \ -p 3306:3306 \ -d mysql:8.04.2 建表与初始化数据
-- 文件路径:01_create_table.sql CREATE DATABASE IF NOT EXISTS student_db; USE student_db; DROP TABLE IF EXISTS student; CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, student_no VARCHAR(20) NOT NULL, email VARCHAR(100), enroll_year INT );先插入一批测试数据,覆盖各种边界情况:
-- 文件路径:02_insert_data.sql INSERT INTO student (name, student_no, email, enroll_year) VALUES ('张三', '20230001', 'zhangsan@example.com', 2023), ('张三丰', '20220015', 'zhangfeng@example.com', 2022), ('张伟', '20210088', 'zhangwei@example.com', 2021), ('李四', '20230022', 'lisi@example.com', 2023), ('王五', '20200033', 'wangwu@example.com', 2020), ('张小明', '20230056', 'zhangxiaoming@example.com', 2023), ('陈静', '20210077', 'chenjing@example.com', 2021), ('李静怡', '20220099', 'lijingyi@example.com', 2022), ('张静', '20230101', 'zhangjing@example.com', 2023), ('100%纯棉短袖', 'P2023001', 'product@example.com', 2023);注意最后一条,故意插入一个包含%的字段值,用来演示 ESCAPE 转义。
4.3 基础 LIKE 查询示例
示例 1:查询所有姓“张”的学生
SELECT id, name, student_no, enroll_year FROM student WHERE name LIKE '张%' ORDER BY enroll_year;这条 SQL 的语义是“以张开头”,匹配结果包括:张三、张三丰、张伟、张小明、张静。如果表里有人叫“老张”,不会被匹配到。
示例 2:查询姓名中包含“静”的学生
SELECT id, name, student_no, enroll_year FROM student WHERE name LIKE '%静%';匹配结果:陈静、李静怡、张静。这条 SQL 是典型的包含匹配,也是实际业务中最常见的用法。
示例 3:查询姓“张”且名字一共两个字的学生
SELECT id, name, student_no, enroll_year FROM student WHERE name LIKE '张_';匹配结果只有“张三”和“张静”,因为它们名字是两个字符,且第一个字符是“张”。“张小明”是三个字,匹配不了。
4.4 LIKE 与 ESCAPE 转义示例
示例 4:查询商品名称中包含“100%”的记录
SELECT id, name FROM student WHERE name LIKE '%100\%%' ESCAPE '\';这里的关键在于ESCAPE '\'告诉数据库反斜杠是转义字符。第一个%和最后一个%是通配符,中间\%是真实存在的百分号字符。
如果不加 ESCAPE,写成LIKE '%100%%%',数据库会把它解析成“包含100”后面任意字符,结果完全不可控。
示例 5:SQL Server 中匹配以任意字符开头、后面是“张”的写法
-- SQL Server 兼容写法 SELECT id, name FROM student WHERE name LIKE '_[张]%';在 SQL Server 中,[张]表示匹配单个字符集合中的任意一个。这个语法在 MySQL 里不生效,MySQL 的 LIKE 不直接支持字符集合写法。如果你在 SQL Server 环境使用,这行代码可以更精确地表达“第二个字符是张”的语义。
4.5 NOT LIKE 与组合条件
LIKE 也可以配合NOT使用:
-- 查询姓名中不包含“张”的所有学生 SELECT id, name FROM student WHERE name NOT LIKE '%张%';还可以和AND、OR组合:
-- 查询 2023 年入学且姓名以“张”开头的学生 SELECT id, name, enroll_year FROM student WHERE enroll_year = 2023 AND name LIKE '张%';这些都是非常常见的业务组合,逻辑上并不复杂,重点是避免在组合条件里写出互相矛盾的过滤逻辑。
4.6 如何运行和验证
如果你用的是 MySQL 客户端:
mysql -uroot -p root123 -h 127.0.0.1 < 01_create_table.sql mysql -uroot -p root123 -h 127.0.0.1 student_db < 02_insert_data.sql然后进入交互式客户端逐条执行查询语句,观察返回结果是否符合预期。验证的重点不是“有没有结果”,而是“结果是不是多出来了”或“少掉了”。多出来往往是通配符误用了%,少掉了往往是大小写或排序规则的问题。
4.7 运行结果与效果验证
以示例 1 为例,预期输出大致如下:
+----+--------+------------+-------------+ | id | name | student_no | enroll_year | +----+--------+------------+-------------+ | 1 | 张三 | 20230001 | 2023 | | 2 | 张三丰 | 20220015 | 2022 | | 3 | 张伟 | 20210088 | 2021 | | 6 | 张小明 | 20230056 | 2023 | | 9 | 张静 | 20230101 | 2023 | +----+--------+------------+-------------+判断成功的标准很简单:返回的行和预期数据一致,不包含“老张”“张二丰他爹”这类不符合前缀规则的记录。如果结果不对,优先按下面顺序排查:
- 检查表里的真实数据,确认自己以为的数据和实际存储的数据一致。
- 检查字段排序规则(collation),确认是不是大小写或全角半角导致匹配失败。
- 去掉
%缩小范围,先跑一个精确匹配,确认命令本身没有拼错。
5. LIKE 与性能:为什么 % 开头会让查询变慢
5.1 从全表扫描开始说起
LIKE 最大的性能杀手是前导通配符,也就是LIKE '%关键词'或LIKE '%关键词%'这种写法。
原因要从 B+ 树索引的结构说起。数据库索引本质上是按照字段值排序后建立的一种快速查找结构。对于普通索引,在条件col = '关键词'时,数据库可以直接从索引树的根节点一路找到叶子节点,定位到匹配记录。这就是索引带来的加速效果。
但是对于LIKE '%关键词%',%在开头,数据库不知道关键词后面是什么,更不知道前面是什么,所以无法利用索引进行范围扫描。它只能老老实实地从表的第一个数据块开始,逐行读取、逐行尝试匹配,这就是全表扫描(Full Table Scan / Sequential Scan)。
数据量小的时候无所谓,几千条记录扫就扫了。但到了千万级别,全表扫描的磁盘 I/O 和 CPU 开销会迅速放大,一条 LIKE 查询拖垮一个接口的事情在真实项目里并不少见。这也是“慢 SQL 优化”话题中,LIKE '%x%'长期霸榜的原因。
5.2 哪一种 LIKE 写法能走索引
在 MySQL 的 InnoDB 存储引擎下,规则大致如下:
| LIKE 写法 | 是否能走索引 | 原因 |
|---|---|---|
LIKE '关键词%' | 可以 | 前缀是确定的,可以用索引范围扫描 |
LIKE '%关键词' | 基本不行 | 前缀通配,需要全表扫描 |
LIKE '%关键词%' | 基本不行 | 前后都通配,需要全表扫描 |
LIKE '_关键词%' | 基本不行 | _本身也可能让前缀不确定 |
这里的“基本不行”是有例外的,比如 MySQL 的 ICP(Index Condition Pushdown)和覆盖索引可以带来部分优化,但整体结论仍然稳健:前导通配符会让索引失效。
5.3 查看执行计划
在任何数据库里,遇到 LIKE 查询变慢,第一步都是看执行计划。
MySQL 查看方式:
EXPLAIN SELECT id, name FROM student WHERE name LIKE '张%';重点关注type列。如果看到ALL,说明是全表扫描;如果看到range或ref,说明走的是索引范围扫描。
对于EXPLAIN结果,有一个常见误区是:只要看到key列有值,就以为走了索引。实际上type = ALL时即使key列有值,也往往只是用到覆盖索引做扫描,并没有真正利用索引加速定位。更稳妥的判断指标,是看rows列是否明显小于总行数。
5.4 利用前缀索引或生成列优化
如果业务一定要做包含匹配,但数据量又大,有几种替代思路。
方案一:把后缀匹配转换成前缀匹配。比如要查询LIKE '%abc',可以在表中增加一个反转列(reversed_col),存储原字段反转后的值,并给反转列建索引。查询时改成WHERE reversed_col LIKE 'cba%',因为反转后的数据,前缀是确定的,就可以走索引了。
MySQL 8.0 支持函数索引(Functional Index),可以直接给REVERSE(col)建立索引。SQL Server 支持计算列 + 索引。PostgreSQL 也有表达式索引。思路都是类似的:让模糊匹配变成确定前缀的匹配。
方案二:在 PostgreSQL 中使用 pg_trgm 扩展,通过 GIN 索引支持LIKE '%关键词%'和ILIKE '%关键词%'。这种方法利用了三元组(trigram)的特性,可以在大量文本里做高效的相似度匹配。
方案三:如果业务是全文搜索,且数据量大、更新频繁,可以考虑 MySQL 全文索引(FULLTEXT)、PostgreSQL 全文搜索,或者引入 Elasticsearch 这类专门做检索的中间件。LIKE 在这种场景下的角色应该是“兜底方案”,而不是主角。
6. LIKE 与 SQL 注入:安全风险边界
6.1 SQL 注入为什么会和 LIKE 扯上关系
在数据库管理系统的安全风险里,SQL 注入是长期霸榜的高危问题。而 LIKE 又因为它的“拼接”特性,特别容易被开发者写坏。
最常见的问题代码长这样:
String keyword = request.getParameter("keyword"); String sql = "SELECT * FROM products WHERE product_name LIKE '%" + keyword + "%'"; Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery(sql);这段代码的问题非常明显:用户输入的keyword被直接拼进 SQL 字符串。如果用户输入:
'; DROP TABLE products; --拼接后的 SQL 就变成了:
SELECT * FROM products WHERE product_name LIKE '%'; DROP TABLE products; --%'攻击者利用注释符把后面的内容注释掉,再插入一条破坏性 SQL。这不是理论上的恐吓,这是 SQL 注入攻击里最经典的案例,任何写过几年 SQL 的开发者都应该有敬畏之心。
6.2 参数化查询是底线
正确的写法,是使用参数化查询(PreparedStatement):
String keyword = request.getParameter("keyword"); String sql = "SELECT * FROM products WHERE product_name LIKE ?"; PreparedStatement ps = connection.prepareStatement(sql); ps.setString(1, "%" + keyword + "%"); ResultSet rs = ps.executeQuery();参数化查询的思路是:把 SQL 语句结构和用户输入的数据彻底分离,数据库先把 SQL 模板解析好,再把参数当作纯数据传入。这样用户输入里的任何内容都只是字符串的一部分,不会改变 SQL 语句的语义。
这里有一个细节要注意:参数化查询解决了 SQL 注入问题,但并没有解决 LIKE 通配符语义问题。如果用户输入%或_,它们仍然会被当作通配符处理,查询出来的结果可能超出预期。所以,参数化查询之外,还需要对用户输入做转义处理。两者是配合关系,不是替代关系。
6.3 其他安全注意事项
- 最小权限原则:执行 LIKE 查询的应用账号,不应该拥有 DROP、DELETE 这类高权限。即使 SQL 被注入了,破坏范围也有限。
- 不要把用户输入直接拼进数据库对象名(表名、列名),包括 LIKE 里的表名。
- 日志里不要打印完整的 SQL 拼接过后的语句,防止敏感信息泄露,也防止调试日志本身成为攻击面。
- 如果提供的是 REST API 接口,建议在参数校验层限制搜索关键词的长度,比如最大 50 个字符,避免恶意构造超长 LIKE 语句。
7. 什么时候该换掉 LIKE,用什么替代
7.1 业务场景对匹配精度有更高要求
LIKE 的能力边界很清晰:只能做“包含”“前缀”“后缀”这类简单的模式匹配。如果业务需要正则表达式级别的匹配,比如手机号格式校验、邮箱格式校验,LIKE 就力不从心了。
这种场景应该使用数据库自带的正则表达式能力:
- MySQL:
REGEXP_LIKE()或REGEXP - PostgreSQL:
~、~*或SIMILAR TO - SQL Server:较新版本支持
LIKE但正则支持不如前两者,需通过 CLR 或数据库层函数实现
但正则表达式的代价是性能通常比 LIKE 更差,尽量不要在核心查询路径上使用。
7.2 全文检索场景
如果业务是做文章搜索、商品描述搜索、日志内容搜索,数据量在百万以上,且需要分词、排序、高亮等功能,LIKE 完全不合适。此时应该考虑:
- MySQL FULLTEXT 索引,配合
MATCH ... AGAINST语法。 - PostgreSQL 全文搜索,支持 tsvector、tsquery,能处理中文分词配置。
- Elasticsearch,适合独立搜索引擎架构,功能最强大但也要额外维护一套集群。
7.3 数据量较小且匹配简单时
如果数据量只有几百条或几千条,LIKE 仍然是最简单可靠的方案。不要为了追求“高级”而给一个小系统引入全文搜索引擎,那是过度设计。开发效率和可维护性同样重要,在数据量可控的范畴内,LIKE 的可读性和通用性没有任何替代方案能比。
7.4 一个实用的选型决策思路
简单归纳一下:
| 场景 | 推荐方案 |
|---|---|
| 少量数据,模糊查询 | LIKE |
| 大量数据,前缀匹配 | LIKE + 索引,或函数索引 |
| 大量数据,包含匹配 | 生成列 + 索引 / pg_trgm / 全文索引 |
| 复杂模式校验 | 正则表达式 |
| 大规模全文搜索 | 全文索引 / Elasticsearch |
| 关键词搜索 + 排序 + 高亮 | Elasticsearch 或专业搜索组件 |
8. LIKE 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| LIKE 查询结果为空,但确认数据存在 | 大小写不匹配或字段排序规则不同 | 用SELECT直接查看原始数据;检查字段 collation | 使用UPPER(col) LIKE UPPER('%关键词%'),或调整排序规则 |
| 匹配到了预期之外的数据 | %、_被当作通配符解释了 | 检查用户输入是否包含通配符 | 对%、_、\做转义,配合ESCAPE子句 |
| 查询非常慢 | %关键词%导致全表扫描 | 执行EXPLAIN查看type=ALL | 改为前缀匹配、增加生成列索引,或换全文检索 |
| 同样 SQL 在 MySQL 有结果、SQL Server 没有 | 两库的排序规则和 LIKE 行为不同 | 查看数据库 collation | 统一排序规则,或显式使用COLLATE |
| 包含中文时匹配不到 | 编码问题,字符集不一致 | 检查连接字符集和表字符集 | 统一使用utf8mb4 |
| NOT LIKE 排除不掉某些记录 | 字段值为 NULL,NOT LIKE 对 NULL 返回 NULL | 检查空值数据 | 条件中显式加AND col IS NOT NULL |
| 后端拼接 SQL 被注入 | 用户输入直接拼入 SQL 语句 | 检查代码中的字符串拼接 | 使用参数化查询 |
| 搜索关键词包含空格导致匹配异常 | 字段存储了多余空格 | 去掉首尾空格再比较 | 存储时使用TRIM,查询时同样处理 |
9. 最佳实践与工程建议
9.1 别让 LIKE 出现在核心链路的全表扫描里
这是最重要的一条。凡是用户直接触发的查询路径,都要对 LIKE 的写法保持敏感。能写成前缀匹配就尽量写前缀匹配。写不了,就评估数据量,数据量大就引入替代方案。不要把LIKE '%关键词%'当成万能工具。
9.2 用户输入的转义不能省
后端只要接受用户输入并用 LIKE 查询,就必须考虑转义逻辑。写成一个公共工具方法,所有模块复用,而不是每个 SQL 里手写一遍转义逻辑。这个工具方法应该对\、%、_三个字符都做处理,并搭配ESCAPE子句。
9.3 索引设计要结合 LIKE 的实际使用
如果业务里频繁出现WHERE name LIKE '张%',那么给name字段建立普通 B+ 树索引就能生效。如果还有WHERE enroll_year = 2023 AND name LIKE '张%',更适合创建组合索引(enroll_year, name),注意列顺序。
如果频繁出现反向前缀匹配LIKE '%com',可以创建函数索引,比如 MySQL 8.0 的CREATE INDEX idx_reverse_email ON student ((REVERSE(email)))。
这些索引设计的小技巧,能让 LIKE 查询在不改变业务逻辑的情况下快一个数量级。
9.4 把查询结果纳入监控和预警
在慢 SQL 统计中,LIKE 查询往往占比很高。建议在数据库端开启慢查询日志,定期分析哪些 LIKE 查询耗时最长,针对 Top N 做优化。优化的优先级,建议参考“执行频率 x 单次耗时”这个指标,而不是只看单条 SQL 的绝对耗时。
对于频繁执行的关键 LIKE 查询,还可以加一层缓存。但要注意缓存一致性,数据更新后要主动失效,别让用户看到旧数据,这比缓存命中率更重要。
9.5 写出可读的 SQL
简单说几个 LIKE 相关 SQL 的代码规范:
- 通配符尽量写在参数一侧、拼接处集中管理,不要散落在多个 SQL 里。
- 使用
ESCAPE '\'时保持一致性,同一项目的转义风格统一。 - 涉及相同模式的逻辑,抽取成数据库视图,或者后端公共查询方法,避免重复写。
- 在复杂查询里,给 LIKE 条件加注释,说明匹配意图,方便后来的人维护。
9.6 防止过度设计
最后一条建议,是别把简单问题复杂化。如果表只有几万行,LIKE '%关键词%'也能跑得很快,就没有必要为了“性能优化”去强行引入函数索引或者全文检索。工程上最重要的判断标准是:当前的数据量、查询频率和用户体验,决定当前需要什么方案。永远不要为了炫技而降低系统的可维护性。
10. 总结与后续学习方向
关于 LIKE,核心的知识点可以压缩成三句话:
第一,它是一个模式匹配操作符,%表示任意长度,_表示单个字符,ESCAPE用来匹配通配符本身。
第二,它很好用,但有性能边界和安全边界。前导通配符会让索引失效,用户输入不转义会让 SQL 注入有机可乘。
第三,它不是唯一的模糊匹配方案。前缀匹配、函数索引、全文检索、正则表达式,各自有各自的最佳场景,选型要结合数据量和业务复杂度。
如果你刚开始学 SQL,可以沿着“LIKE 基础 → 通配符细节 → 执行计划 → 索引优化”这条线继续深入,每一步都动手建表、造数据、跑查询。尤其建议自己造一张百万行的测试表,看看LIKE '前缀%'和LIKE '%后缀'的执行计划差异有多大。实践带来的体感,比看十篇教程都更有用。
如果你是在生产环境维护数据库,可以把这篇文章里关于ESCAPE、参数化查询、函数索引的内容作为团队内部 review 的 checklist,减少自己和同事在 LIKE 上栽跟头的概率。
LKE 虽小,却贯穿了 SQL 语法、索引原理、安全编码和查询优化好几个核心方向。把一个小知识点挖深了,比囫囵吞枣刷一百个 SQL 面试题更有价值。下一步,建议去查一下你正在用的数据库版本,对照本文的示例实际跑一遍,看看执行计划里到底发生了什么。