
1. 先想清楚字段“包含”到底有哪几种写法写SQL的人应该都躲不开一个场景前端传过来一个关键字产品告诉你“这个列表里的内容只要标题或者描述里包含这个关键字就都给我查出来”。乍一听这不就是一句 LIKE 的事吗真正写起来才发现字符集、大小写、通配符、索引、不同数据库函数命名每一项都能让你踩一脚泥。这篇文章专门聊“判断一个字段是否包含某个数据”这件事把常用方法、底层逻辑和性能上的取舍一起说清楚。如果你刚接触SQL可能以为答案只有LIKE如果你写了几年业务SQL大概率也遇到过“我明明用LIKE查出来了换了个环境怎么就不行”的疑问。这篇内容尽量做到知道怎么写能跑也知道怎么写能快。1.1 子串包含、前缀匹配、集合包含、全文命中先分清再动手判断字段是否包含数据最容易犯的错误是“一上来就写LIKE”。其实“包含”在业务里至少有四种不同的意思对应的写法和性能表现完全不一样。第一种是子串包含也就是某个字符串片段完整地出现在字段值里。比如字段值“abc123”判断它是否包含“c12”。这种需求通常用 LIKE 或位置函数解决也是本文的重点。第二种是前缀或后缀匹配比如判断手机号是否以“138”开头或者文件名是否以“.pdf”结尾。这种用 LIKE 的138%和%.pdf就能做而且前者还能走索引性能往往不算差。第三种是集合包含字段里存的是逗号分隔的编号比如1,2,3要判断是否包含2。简单用LIKE %2%会引发误匹配因为12里也有2。这时需要先想清楚字段的设计规范和边界分隔符。第四种是全文命中即对一篇文章、一段描述做自然语言搜索判断是否包含某个词或短语。数据量小的时候 LIKE 也能顶但数据量一大LIKE 的扫描成本就直线上升这时需要引入全文索引。这四种语义对应的方案不同优化的方向也不同。本文会把它们分开讲避免你拿着子串包含的写法去处理全文搜索的需求或者反过来。1.2 不同数据库的“包含”函数名对照不同数据库对“判断字符串包含”这件事的命名差异特别大。同一个需求在 A 库叫 CHARINDEX在 B 库叫 INSTR在 C 库叫 STRPOS。这也是很多SQL玩家跨库迁移时最容易懵的地方。我从实际经验里整理了一张对照表方便你一眼定位数据库判断子串包含的函数返回结果典型写法MySQL 系INSTR / LOCATE子串第一次出现的位置从1开始找不到返回0WHERE INSTR(字段, 关键字) 0SQL Server 系CHARINDEX子串第一次出现的位置找不到返回0WHERE CHARINDEX(关键字, 字段) 0PostgreSQL 系STRPOS / POSITION子串第一次出现的位置找不到返回0WHERE STRPOS(字段, 关键字) 0部分商业数据库INSTR子串出现的位置找不到返回0WHERE INSTR(字段, 关键字) 0看到没有规律是返回位置大于0就是包含。这种方式比 LIKE 更直接也适合在 WHERE、CASE WHEN 里继续做逻辑判断。不过要注意参数顺序有的函数是“字符串在前子串在后”有的是“子串在前字符串在后”。这个细节我第一次写就踩过稍后展开讲。2. 最常用的LIKE判断包含通配符和大小写都别忽略2.1 LIKE 用 % 和 _ 做模糊包含写法与边界LIKE 是大部分人第一个想到的方案也是SQL标准语法。它通过通配符来判断字符串匹配模式。%代表任意长度的字符包括零个字符_代表任意一个字符。判断字段是否包含某个数据最基础的写法是SELECT id, title FROM article WHERE title LIKE %SQL%;这条语句的意思是只要 title 字段里出现了“SQL”三个字符就返回这行。由于两个%分别放在前后它既能匹配“SQL入门”也能匹配“深入理解SQL优化”还能匹配“SQL”相当于“包含”语义。还有一个细节LIKE 的%匹配的是字符序列而不是独立单词。所以LIKE %SQL%会匹配到 “MySQL” 或者 “NoSQL” 里的 “SQL”。如果你的业务要求的是“字段值包含某个独立单词”比如必须包含 “sql” 这个完整单词而不是 “mysql” 里的子串那 LIKE 其实不够严谨得配合分隔符拆解或者使用全文索引。_用得少但在固定格式的字段里很实用。比如判断一个编码字段第3位是否为“A”可以写WHERE code LIKE __A%;不过这句话已经不属于单纯的“包含”了而是“按位置匹配”。平时判断包含还是以%关键字%为主。2.2 大小写、空格和特殊符号LIKE 的实际表现LIKE 对大小写的敏感程度取决于数据库的排序规则。很多初学者会遇到“明明数据库里有数据LIKE 就是查不出来”的情况十有八九是大小写规则在作怪。以常见的 MySQL 为例默认排序规则通常不区分大小写所以LIKE %abc%能匹配到ABC。但在 PostgreSQL 里LIKE默认是区分大小写的LIKE %abc%查不到ABC必须用ILIKE或者LOWER(字段) LIKE %abc%。SQL Server 的表现则由库级别的排序规则决定Chinese_PRC_CI_AS中的CI是 Case Insensitive也就是不区分大小写如果是CS就区分。还有一个容易被忽略的坑尾部空格。很多定长字段比如CHAR类型存储时会在末尾补空格。当你用LIKE %关键字%查询时因为这些空格在中间和末尾%能匹配到它们所以通常没问题。但当关键字本身包含空格时比如判断name是否包含“张 三”如果原始数据里有多个连续空格LIKE 可能无法命中。这种情况建议先清洗数据或者用正则表达式来兼容。特殊符号也要注意。%和_本身如果是业务关键字比如你要查字段里有没有“100%”直接写LIKE %100%%是不对的因为后一个%会被当成通配符。正确的做法是用ESCAPE指定转义符WHERE remark LIKE %100!%% ESCAPE !;这里!是自定义的转义字符它告诉数据库!%表示一个真实的百分号而不是通配符。2.3 LIKE 判断包含时最常见的性能坑LIKE 判断“是否包含”虽然简单但性能坑也最明显。关键在于通配符放的位置。LIKE 关键字%属于前缀匹配数据库有机会使用 B-Tree 索引前提是列上有普通索引并且排序规则支持。这种查询性能还可以适合“以什么开头”的场景。LIKE %关键字和LIKE %关键字%属于后缀匹配和包含匹配因为目标字符串可能出现在字段值中间任意位置数据库只能扫描整列然后对每个值做一次子串匹配。这在数据量小的时候看不出问题一旦到了几百万行就会把查询速度拖成几十秒甚至几分钟。举个很常见的现象订单表的备注字段做了模糊搜索WHERE remark LIKE %退款%。表里有两千万行这个查询会全表扫描哪怕你在备注字段上建了普通索引也没用因为当通配符在左侧时B-Tree 索引无法定位起始位置。所以如果你判断包含的行为是高频率、大数据量的查询不要一上来就用LIKE %xxx%建议先看后面的位置函数、全文索引和表达式索引方案。3. 用位置函数判断字段是否包含CHARINDEX、INSTR、LOCATE、STRPOS3.1 各数据库位置函数对照表与返回结果位置函数的思路很简单判断子串是否存在本质上是看它在字符串中出现的位置。返回 0 表示不存在返回大于 0 的数字表示存在。利用这个特性可以写出比 LIKE 更显式的包含判断。以判断email字段中是否包含为例-- MySQL 系 WHERE INSTR(email, ) 0; -- SQL Server 系 WHERE CHARINDEX(, email) 0; -- PostgreSQL 系 WHERE STRPOS(email, ) 0;位置函数的优势在于它返回的是一个数值你可以把它用在SELECT列表里提取子串也可以用在CASE WHEN里做分支判断。比如SELECT email, CHARINDEX(, email) AS at_pos, CASE WHEN CHARINDEX(, email) 0 THEN 合法 ELSE 非法 END AS email_status FROM user_info;这种写法在数据清洗和报表SQL里非常常见LIKE 做不到这种“只判断包含还同时给出位置”的灵活性。3.2 参数顺序和NULL值两个容易翻车的地方位置函数最大的坑是参数顺序不一致。CHARINDEX和LOCATE的参数顺序是“子串在前原串在后”INSTR和STRPOS是“原串在前子串在后”。我见过不止一次把 SQL Server 的经验惯性带到 MySQL结果把两个参数写反导致查询结果完全错误甚至报参数类型错误。给你一个记忆技巧INSTR(str, sub)可以理解为 “In String”也就是“在字符串里找子串”所以原串在前CHARINDEX(sub, str)可以理解为“字符索引”你要找的字符子串放前面稍微别扭但符合它的命名逻辑。写之前最好先看对应的官方文档。另一个坑是 NULL 值。如果字段本身是 NULLINSTR、CHARINDEX、STRPOS这些函数返回的都是 NULL而不是 0。也就是说WHERE INSTR(NULL, abc) 0的结果不是 FALSE而是 UNKNOWN在 WHERE 里最终会被过滤掉并不会有问题。但如果你的业务逻辑是“NULL也算不包含”就要显式处理WHERE INSTR(COALESCE(remark, ), 关键字) 0;这样能确保 NULL 情况下返回 0判断为不包含。3.3 位置函数配合截断和条件判断的实际例子位置函数还有一个LIKE给不了的能力提取子串。比如某个字段的格式是“国家-城市-详细地址”要判断该字段是否包含“上海”并把“上海”后面的详细地址取出来。可以用位置函数定位SELECT address, CASE WHEN LOCATE(上海, address) 0 THEN SUBSTRING(address, LOCATE(上海, address)) ELSE 不包含 END AS extracted_part FROM customer_info;这个例子虽然简单但思路很实用位置函数返回的点位信息可以直接传给SUBSTRING去截取。这种写法在做日志解析、标签拆分、地址标准化时经常用到。从性能角度看位置函数和LIKE %xx%执行计划通常都走全表扫描因为无法用普通索引。但位置函数有一个好处它是确定性的函数在部分数据库里可以配合表达式索引使用。比如你想优化“某个字段包含特定前缀”的查询可以先建一个INSTR(字段, 规则)的表达索引不过这已经属于函数索引的范畴后面单独讲。4. 正则表达式判断包含REGEXP 与 POSIX 风格4.1 正则包含的典型场景从简单模式到复杂规则LIKE 和位置函数适合判断“某个固定字符串是否出现”。但如果规则变得复杂比如字段是否包含一个5位数字是否包含手机号片段是否包含以字母开头、后跟数字的内容是否包含中文字符是否包含邮箱格式的局部匹配这些场景用 LIKE 写起来会非常痛苦通配符组合一长阅读性就很差。正则表达式就是为这种“更具体的包含规则”而生的。例如你要判断一个文本字段里是否包含连续4位数字-- MySQL 系 WHERE text_field REGEXP [0-9]{4}; -- PostgreSQL 系 WHERE text_field ~ [0-9]{4};这样就比写出一长串LIKE %0%1%2%3%要清晰得多。正则表达式的核心不是“找某一个固定的词”而是“匹配一类模式”。判断是否包含时它天然支持在任意位置命中不需要像 LIKE 那样手动加%。4.2 不同数据库的正则写法差异正则的写法在不同数据库之间差异很大。MySQL 从 8.0 开始推荐REGEXP_LIKE(字段, 正则)老版本用REGEXP或RLIKE。PostgreSQL 使用 POSIX 风格~表示匹配正则~*表示忽略大小写匹配。SQL Server 原生没有正则表达式函数需要用LIKE加ESCAPE做近似处理或者通过公共语言运行时注册自定义函数但这通常不是首选。常见的包含判断写法如下-- MySQL 8.0 WHERE REGEXP_LIKE(name, ^张[0-9]$); -- PostgreSQL WHERE name ~ ^张[0-9]$; -- 通用写法配合 LOWER 忽略大小写 WHERE LOWER(name) ~ ^zhang[0-9]$;正则里也支持“或”关系。比如判断一个字段是否包含“苹果”或“香蕉”WHERE remark REGEXP 苹果|香蕉;在不用正则时你需要写两句 LIKE 再 OR 起来。正则让表达式更紧凑不过也更考验维护者对正则语法的熟悉程度。4.3 用正则判断包含时该不该考虑性能正则表达式的表现力和复杂度通常和性能成反比。正则引擎需要对目标字符串做逐字符匹配模式越复杂回退次数越多CPU 消耗越高。在几万行的小表上跑一跑没问题在千万级的表上用REGEXP .*[0-9]{4}.*做过滤大概率会把数据库烧得很高。我的建议是正则判断包含适合用在小数据量、结果集已经被其他条件收窄的场景。比如先通过索引条件把订单表缩小到当天的几万行再用正则做复杂的备注分类。如果正则直接作用在主表过滤条件上并且表数据很大一定要先在测试环境执行EXPLAIN看扫描行数再决定是否采用。另外正则表达式本身不会导致索引失效因为它已经没有办法走索引。除非你用正则判断前缀比如^138部分数据库可能会优化成前缀匹配但依赖数据库内部实现不能当作保证。真正常见的性能问题是把字段用函数包裹之后再匹配比如WHERE LOWER(name) ~ abc这会让该列的普通索引彻底失效。所以能在大查询里避免正则就尽量避免不能避免时优先缩小数据范围。5. 大数据量文本的“包含”方案全文索引与专业搜索5.1 为什么数据量大时LIKE和位置函数都不够当一张表的文本数据量特别大比如文章表、商品描述表、日志表判断字段是否包含某个词就不再是简单的LIKE问题。原因有两个第一全表扫描成本太高。LIKE %关键字%需要对每一行的字段值都做一遍子串查找数据量翻倍耗时也近似翻倍。第二LIKE 的匹配粒度是“字符连续出现”。但实际搜索场景里用户可能搜索“SQL优化”希望匹配包含“SQL性能优化”的文章单纯用 LIKE 按“SQL优化”四个连续字符去匹配结果是查不到的。真正的搜索需要把文本拆成词建立倒排索引然后做词汇匹配。这时候就要用全文索引。全文索引的原理可以类比书的末尾索引它预先处理文本提取出每个词并记录这个词出现在哪些文档的哪些位置。查询时直接查这个反向列表不需要扫原文。所以全文索引在“字段是否包含某个词/短语”这类需求上有数量级的性能优势。5.2 主流数据库全文索引的包含查询写法在 MySQL 中给字段加上FULLTEXT索引后用MATCH ... AGAINST做包含查询。比如判断标题是否包含“数据库”这个词ALTER TABLE article ADD FULLTEXT INDEX idx_title_content(title, content); SELECT id, title FROM article WHERE MATCH(title, content) AGAINST(数据库 IN BOOLEAN MODE);这里的表示必须包含。如果不加默认是自然语言模式会把包含“数据库”的文档按相关度排序返回这更贴近搜索引擎体验。在 SQL Server 中全文字段有CONTAINS关键字SELECT id, title FROM article WHERE CONTAINS(title, 数据库);还可以用CONTAINSTABLE把相关度也查出来。在 PostgreSQL 中全文检索更讲究分词和向量。写法大致是SELECT id, title FROM article WHERE to_tsvector(simple, title) to_tsquery(数据库);这里to_tsvector把标题转换成词向量to_tsquery把查询词转成查询向量表示匹配。如果想忽略大小写simple配置本身就能覆盖一部分需求但中文需要额外的分词扩展。5.3 全文索引的适用边界和常见误解全文索引虽然快但不是万能的。一个常见误解是“我建了全文索引LIKE %关键字% 是不是也能变快”。答案是并不会。LIKE 和全文索引是两条完全独立的路径普通 LIKE 查询不会因为你建了 FULLTEXT 索引而走全文索引。你必须改写 SQL显式用MATCH AGAINST或CONTAINS等全文函数。全文索引也有自身的限制。MySQL 的FULLTEXT默认有一个最小索引长度MyISAM 是4个字符InnoDB 是3个字符。如果搜索的关键词只有1到2个字符默认会忽略导致查不出结果。这个参数可以调比如把innodb_ft_min_token_size改小但会明显增大索引体积和维护成本。中文场景还有一个头疼的问题默认分词把连续汉字当词搜索“数据库”可能会匹配“数据库”这三个连续字符但搜索“库”或“数据”表现不稳。很多生产环境在中文全文搜索上直接用扩展组件或者干脆把搜索业务放到专门的搜索引擎里。所以判断字段包含数据时要结合业务规模和技术栈选择方案。6. 特殊字段的包含判断JSON、数组、逗号分隔6.1 JSON字段判断“是否包含某个键值”现代数据库很多都支持 JSON 类型JSON 里的“包含”和普通字符串完全不同。你要判断的不是“字符串中是否有某段字符”而是“JSON 结构中是否存在某个键、某个值或者某个数组元素”。MySQL 里可以用JSON_CONTAINS。判断 JSON 数组是否包含某个值是SELECT id, attributes FROM product WHERE JSON_CONTAINS(attributes, 红色);注意值要写成合法 JSON 字符串比如包含颜色时参数是红色而不是红色。判断 JSON 对象里是否包含指定键也可以用JSON_CONTAINS_PATHWHERE JSON_CONTAINS_PATH(attributes, one, $.color)这段的意思是检查attributes里是否有$.color这个路径。one表示只要任意一个路径存在就返回真all则是要求全部存在。PostgreSQL 处理 JSON 更偏操作符风格。比如jsonb字段可以用?判断顶层键是否存在用判断是否包含某个子对象WHERE attributes ? color; WHERE attributes {color:红色};JSON 是一种结构化数据用对函数之后性能远好于把 JSON 转成字符串再 LIKE。因为 JSON 函数了解字段内部的逻辑结构而 LIKE 只能按字符硬扫。6.2 数组与集合类型用专门的包含操作符类似 Postgres 这类数据库原生支持数组类型。判断一个数组字段是否包含某个值不能写LIKE而应该用数组操作符WHERE tags ARRAY[数据库];若想知道数组是否与另一个数组有交集可以用WHERE tags ARRAY[数据库, SQL];这类操作符背后有 GIN 索引支持比把数组转成逗号字符串再做 LIKE 快得多语义也更准确。如果你在设计表时能选择数组类型就尽量不要用逗号拼串。数据库已经提供了结构化能力再用字符串模拟集合只会平白增加判断难度。6.3 逗号拼串字段LIKE能查但别乱用现实系统里因为历史原因很多字段存的是逗号分隔的编号比如tag_ids 3,5,12。要判断这个字段是否包含编号5直接写WHERE tag_ids LIKE %5%这是错误的。它会匹配到 15、25、50 等多个非法结果。正确思路是给每个编号加上前后分隔符再做匹配或者在查询时把tag_ids也加上分隔符一起判断WHERE CONCAT(,, tag_ids, ,) LIKE %,5,%;这个写法会在字段自身前后各补一个逗号然后在目标编号前后也加逗号确保匹配的是完整编号。它还顺带解决了“编号出现在开头或末尾”的问题。比如tag_ids 5时原字段LIKE %,5,%是匹配不到的但CONCAT(,, 5, ,) ,5,再匹配%,5,%就能命中。不过这仍然是一个全表扫描写法数据量大时最好改表设计用关联表替代逗号拼串。如果业务上非得在嵌套字段里判断集合包含也可以考虑数据库的 JSON 或数组能力避免在字符串边缘玩花活。7. 判断包含的SQL性能优化与索引改写思路7.1 前导通配符、函数包裹字段索引失效的两大元凶判断字段包含数据的查询性能翻车一般由两个原因造成。第一个是前导通配符。LIKE %关键字%和LIKE %关键字无法使用 B-Tree 索引因为数据库不知道匹配的起点只能扫描所有的键值。LIKE 关键字%虽然可以走索引但它只解决“以某前缀开头”的包含场景不是真正的任意位置包含。第二个是函数包裹字段。很多人为了让大小写不敏感写成WHERE LOWER(title) LIKE %数据库%;或者为了拼接逗号判断写成WHERE CONCAT(,, tag_ids, ,) LIKE %,5,%;这在逻辑上没错但数据库面对的是“经过函数计算后的结果”普通索引无法作用于原始字段于是优化器放弃索引走全表扫描。这是“判断包含”类查询性能变差最常见的原因之一。所以写 SQL 时看一看 WHERE 条件的字段有没有被函数包住、有没有前导%。只要这两个特征出现了就意味着这条查询大概率要做好承受全表扫描的准备。7.2 表达式索引和反向列把模糊包含变成走索引的查询既然函数包裹字段会让索引失效那就直接给“函数计算结果”建索引。这就是表达式索引也叫函数索引。在一些数据库里你可以给LOWER(title)建索引后续查询WHERE LOWER(title) LIKE abc%就能走索引。对于判断是否包含的场景表达式索引依然只能解决前缀匹配但至少比全表扫描强。PostgreSQL 的写法很直接CREATE INDEX idx_lower_title ON article (LOWER(title));MySQL 支持在索引定义里用表达式但要求较新版本ALTER TABLE article ADD INDEX idx_lower_title ((LOWER(title)));另一招是“反向列”。要优化LIKE %abc这种后缀包含可以在业务层把字段反转后存储为另一列然后查询时改成LIKE cba%。比如原字段url example.com/download/file查后缀是否包含“file”反转成elif/dload/moc.elpmaxe再查前缀LIKE elif%。这个思路看起来绕但在某些超大型表上能把全表扫描变成索引范围扫描。前提是业务可以接受额外存储列并处理好数据同步。7.3 实在不适合SQL的场景缓存、拍平表、搜索引擎判断一个字段是否包含某个数据并不一定非要在 SQL 层硬扛。数据量大到一定程度业务也许应该换一种架构。比如搜索需求特别复杂的场景可以用专门的搜索引擎把需要被搜索的字段提前同步过去把“数据库 SQL 包含判断”变成“搜索引擎的全文检索”。这样数据库的压力就小很多。如果不想引入中间件也可以对热点数据做缓存把频繁使用的查询结果提前算好放进去缺点是数据一致性需要额外处理。还有一个思路是“拍平表”。比如订单的标签存储在逗号分隔字段里你每天都按标签筛选订单。与其每次在业务表里做字符串判断不如把“订单ID 标签”拆成一张子表。这张子表里每行是一个标签查询时直接WHERE tag 5瞬间从“字符串包含”变成“等值匹配”可以利用普通索引性能提升非常明显。所以最终判断“SQL里如何判断字段是否包含数据”之前先问自己三个问题数据量多大判断频率多高包含语义是哪种答案不同方法完全不同。8. 常见问题与排查技巧实录8.1 查不到数据大小写、字符集、隐藏字符在业务SQL里最让人头疼的“包含”问题不是报错而是查不到预期数据。我排查过很多次原因集中在三块。第一是大小写。MySQL 默认排序规则可能不区分大小写但 PostgreSQL 区分SQL Server 取决于库的排序规则。如果你在 A 环境测试正常换到 B 环境就查不到先检查库/表的排序规则最省事的办法是统一写LOWER(字段) LIKE LOWER(%关键字%)代价是索引失效适合小表。第二是字符集。字段是中文查询条件是英文或数字或者相反可能出现字符集转换导致匹配失败。比如表的字符集是latin1存中文时本身已经乱码那你再怎么写LIKE %中文%也匹配不到正常的中文关键词。建议建表时统一使用支持中文的字符集并确认连接字符集与表字符集一致。第三是隐藏字符。从 Excel 粘贴过来的数据可能带了回车换行、制表符或者不可见字符。这些字符肉眼看不见但 LIKE 是按字符匹配的多了任何一个隐藏字符都会使包含判定失败。排查时可以先把可疑字段查出来看一眼十六进制值或者用REPLACE清理常见隐藏字符。8.2 查出多余数据%和_没转义、空字符串匹配包含查询最常见的问题是“匹配结果比预期多”。比如想查包含“50%”的字段没加转义LIKE %50%%会匹配所有包含“50”后面任意内容的行。这种问题用ESCAPE可以解决。更隐蔽的是_它代表任意一个字符所以LIKE %A_1%会匹配到AB1而不是只匹配A_1。如果你的业务关键字里本身有下划线千万记得ESCAPE。还有空字符串。LIKE %%在大多数数据库里会匹配所有非 NULL 的字符串包括空字符串。如果你要过滤掉包含空内容的记录判断条件应该写成字段 IS NOT NULL AND 字段 而不是纠结LIKE %%的返回结果。8.3 查询效率突然变差用执行计划定位一条“判断字段包含”的SQL刚开始运行很快数据量到了一定级别突然变慢。这种时候不要靠猜直接看执行计划。最核心的两个指标扫描方式是不是ALL全表扫描扫描行数是不是接近表的总行数。如果是说明查询没有可用索引。再看 WHERE 条件里是不是有 LIKE 前导通配符、函数包裹字段、或者隐式类型转换。比如字段是字符串类型你传了一个数字进去数据库会把字段转成数字再比较索引失效。排查包含查询的性能问题基本就是这几个方向。如果需要保留“任意位置包含”的查询能力可以考虑全文索引、表达式索引或者从业务上限制用户必须输入较长关键字、配合最小长度过滤避免短关键字引爆全表扫描。8.4 一个可以直接照抄的判断包含的SQL模板最后给你一个综合模板用来应对“判断字段是否包含某个数据”的日常需求。它集合了几种常见写法你可以按实际情况选择-- 1. 最基础LIKE 任意位置包含适合小数据量 SELECT id FROM table_name WHERE col_name LIKE %关键字%; -- 2. 带转义关键词里包含 % 或 _ 时使用 SELECT id FROM table_name WHERE col_name LIKE %100!%% ESCAPE !; -- 3. 位置函数返回位置信息适合做 CASE WHEN SELECT id, CHARINDEX(关键字, col_name) AS pos FROM table_name WHERE CHARINDEX(关键字, col_name) 0; -- 4. 正则适合复杂模式 SELECT id FROM table_name WHERE col_name REGEXP 关键字|其他模式; -- 5. 逗号分隔字段避免子串误匹配 SELECT id FROM table_name WHERE CONCAT(,, col_name, ,) LIKE %,关键字,%; -- 6. 全文索引大文本搜索 SELECT id FROM table_name WHERE MATCH(col_name) AGAINST(关键字 IN BOOLEAN MODE);这个模板里第1、2、5条要记住转义和边界问题第3条适合需要位置信息的场景第4条适合复杂模式第6条适合大表文本搜索。至于选哪种核心还是先判断数据量和包含语义再做选择。没有不需要思考的最优SQL只有更贴合场景的方案。