恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

SQL字段包含判断全指南:LIKE、FIND_IN_SET、EXISTS与JSON方案对比

  • 首页
  • 资讯中心
  • /
  • SQL字段包含判断全指南:LIKE、FIND_IN_SET、EXISTS与JSON方案对比

相关资讯

SQL字段包含判断:各数据库写法与性能优化全攻略 2026/9/24 20:04:02
宽带测速不达标?江苏电信用户必看的官方与第三方测速指南 2026/9/24 19:59:02
车载以太网中间件选型指南:SOME/IP、MQTT与DDS深度对比 2026/9/24 19:59:02

最新资讯

PaddleSpeech 服务端 ONNX 推理会话配置详解:读懂 `paddlespeech.server.utils.onnx_infer` 的 `get_sess` 实现
RAID 0/1/5/6/10详解:从原理到实战选型与配置指南
Mac微信双开实操指南:从open -n到AppleScript的完整方案
Python在线考试系统后端开发实战:从模块设计到部署避坑
从一栋实训楼的电气智能化设计,聊聊论文 AI 工具怎么选才不踩坑
Linux共享内存IPC深度解析:从原理到实战,打造微秒级通信

今日推荐

JavaWeb购物车系统实现:基于Session存储的完整工程示例
面向对象综合训练:从图书管理系统掌握封装、继承与多态
Lombok与JDK版本冲突引发NoSuchFieldError:根因排查与修复指南

本周热门

BrewUI:给Homebrew套上图形界面,让macOS软件包管理更简单
BrewUI:让Homebrew包管理变得可视化与高效
公式与文本对齐全攻略:从Word到LaTeX的实用技巧

本月精选

自研推理加速器Redwood:两周内实现PyTorch模型高效部署的实战教程
V4L2摄像头采集实战:从camera_client.rar到出图全流程解析
从“谁发明了钢琴键”到知识问答智能体:RAG与记忆工程实践

SQL字段包含判断全指南:LIKE、FIND_IN_SET、EXISTS与JSON方案对比

发布时间:2026/9/24 20:04:02
SQL字段包含判断全指南:LIKE、FIND_IN_SET、EXISTS与JSON方案对比 1. 先搞清楚你到底在问什么很多刚接触 SQL 的朋友查字段包含某个数据时第一反应就是LIKE然后写出一堆%xxx%。这不奇怪因为LIKE确实是大多数人的启蒙写法。但实际工作中判断字段是否包含某个数据这句话背后的需求千差万别是判断字符串里有没有某个子串还是判断某个值是否在一个逗号分隔的列表里或者是判断 JSON 字段里有没有某个键值对又或者是判断一个多值字段是否包含指定元素不同的场景最优解完全不一样。这篇文章我就把 SQL 里判断包含关系的常用方法系统梳理一遍每种方法的适用场景、坑点、性能表现都讲清楚最后附上我这些年踩坑换来的实战经验。不要指望一个方法通吃所有场景SQL 世界里没有银弹只有在这个场景下最合适的写法。2. 字符串包含判断LIKE、CHARINDEX、POSITION 三兄弟2.1 LIKE 通配符匹配最直观但最容易踩坑LIKE配合%和_是绝大多数人第一反应想到的写法也是在小数据量、临时查询场景下最省事的方案。-- 查询 name 字段中包含 张 的记录 SELECT * FROM users WHERE name LIKE %张%;%表示任意长度的任意字符包括零个字符_表示任意单个字符这个写法有两个典型盲区我见过无数初学甚至工作两三年的开发在这里翻车。盲区一通配符转义问题。如果字段里本身存的是%或_直接LIKE %50%会把 50 前后的任意字符串都匹配出来而不是精确匹配包含50%这个字符串的记录。这时候需要用ESCAPE关键字-- 查询 content 字段中包含 50% 字面量的记录 SELECT * FROM logs WHERE content LIKE %50\%% ESCAPE \;盲区二大小写规则因数据库而异。在 MySQL 默认排序规则下LIKE是不区分大小写的但在 PostgreSQL 里LIKE区分大小写ILIKE不区分SQL Server 则取决于字段的排序规则。同一个业务数据库换了结果就可能不一样。这不是 SQL 标准定义死的是各厂商实现的差异遇到字符串比较类需求第一件事就是确认当前数据库的大小写敏感性。2.2 CHARINDEX / INSTR / POSITION返回位置的精确判断LIKE只能返回布尔结果匹配或不匹配但有时候你需要知道子串到底在第几位或者需要在更复杂的表达式里复用这个判断结果。不同数据库的函数名不一样数据库函数示例SQL ServerCHARINDEXWHERE CHARINDEX(张, name) 0MySQLINSTR/LOCATEWHERE INSTR(name, 张) 0PostgreSQLPOSITION/STRPOSWHERE POSITION(张 IN name) 0OracleINSTRWHERE INSTR(name, 张) 0以 SQL Server 为例SELECT CHARINDEX(张, 张三丰); -- 返回 1 SELECT CHARINDEX(张, 李四); -- 返回 0 SELECT CHARINDEX(张, 王老张, 2); -- 从第 2 位开始找返回 3这个函数的好处是返回的是位置数字可以用来做更精细的截取逻辑比如找出第二个逗号之后的数字单靠LIKE是做不了的。这里有一个性能上的小差异值得注意在 SQL Server 里LIKE abc%前缀匹配可以利用索引CHARINDEX走不了索引但LIKE %abc%本身也走不了索引。所以对于包含任意位置子串的判断两者性能半斤八两没必要为了性能纠结真正影响性能的是你是否需要全表扫描。2.3 正则表达式复杂的包含模式才用它当包含不再是简单的某个固定字符串而是包含一个手机号包含连续 4 位数字包含邮箱格式这类模式匹配时LIKE就力不从心了。-- MySQL 中查询 content 字段包含手机号1开头11位数字的记录 SELECT * FROM messages WHERE content REGEXP 1[0-9]{10}; -- PostgreSQL 写法 SELECT * FROM messages WHERE content ~ 1[0-9]{10}; -- SQL Server 从 2022 版本开始原生支持 REGEXP_LIKE SELECT * FROM messages WHERE REGEXP_LIKE(content, 1[0-9]{10});正则表达式几乎是万能的但它有两个致命缺点第一性能极差。正则是逐字符回溯匹配数据量一大慢得让你怀疑人生。我见过有人对 500 万行的日志表用REGEXP做过滤一条查询跑了几分钟直接把生产库的 CPU 打满。第二写法复杂可读性差。正则的语法本身就有学习门槛团队里不是每个人都看得懂复杂的正则表达式维护成本很高。所以我的原则是能用LIKE解决就不用正则能用普通字符串函数解决就不用正则正则只留给真正非它不可的场景。3. 集合包含判断逗号分隔字段的正确处理方式3.1 FIND_IN_SETMySQL 专属的逗号列表查询业务表里经常出现这样的设计一个字段里存逗号分隔的 ID 或标签比如tag_list 美食,旅游,数码要查包含数码标签的记录。MySQL 提供了FIND_IN_SET它是专为这种场景设计的SELECT * FROM articles WHERE FIND_IN_SET(数码, tag_list) 0;FIND_IN_SET的匹配规则是精确匹配逗号分隔的每一项不会出现LIKE %数码%那种数码产品也被误伤的情况。这是它最大的优势。它的局限也很明显只存在于 MySQL换库就得重写字段里的数据必须用英文逗号分隔空格都不行无法使用索引性能上属于全表扫描举个例子FIND_IN_SET(数码, 数码产品,电脑)返回 0因为逗号分隔的两项分别是数码产品和电脑并没有单独一项叫数码。而LIKE %数码%会返回 1。这是两种完全不同的语义你心里得清楚自己到底要哪种。3.2 字符串拆分后关联通用方案如果逗号分隔字段的数据量比较大或者你需要在包含判断之后进一步做分组统计、关联查询正确姿势是先把字符串拆成多行再做关联或 EXISTS 判断。SQL Server 里的STRING_SPLITSELECT DISTINCT a.* FROM articles a CROSS APPLY STRING_SPLIT(a.tag_list, ,) AS s WHERE s.value 数码;MySQL 从 8.0 开始支持JSON_TABLE或递归 CTE 做拆分但写起来相对繁琐。PostgreSQL 的string_to_arrayunnest是神器SELECT * FROM articles WHERE 数码 ANY(string_to_array(tag_list, ,));这种写法的好处是拆分后的结果可以作为独立的行集继续参与 JOIN、GROUP BY、WHERE 等后续操作逻辑更清晰。缺点是拆分本身有开销不适合高频查询。注意只要字段设计允许把多值数据塞进逗号分隔字段本身就是反模式。如果这个字段要经常做包含查询你就该考虑建一张关联子表。逗号分隔字段适合存着偶尔看的场景不适合天天查的场景。3.3 SQL Server 与 PostgreSQL 的数组/JSON 方案SQL Server 从 2016 开始支持JSON函数PostgreSQL 更是原生支持JSONB和数组类型。如果你的字段本身是 JSON 数组别用字符串函数硬套各数据库都有优雅的原生判断方式SQL Server 中判断 JSON 数组是否包含某个值SELECT * FROM orders WHERE EXISTS ( SELECT 1 FROM OPENJSON(orders.tags) WHERE value vip );PostgreSQL 中判断数组包含-- 数组类型字段 SELECT * FROM users WHERE tags ARRAY[vip]; -- JSONB 数组字段 SELECT * FROM users WHERE tags ? vip; -- 判断 JSONB 数组是否包含指定元素等价写法 SELECT * FROM users WHERE tags [vip];这一块恰恰是最容易让人觉得SQL 能力有限的地方。其实真不是 SQL 不行而是很多人还在用字符串思维处理结构化数据JSON 字段里有索引PostgreSQL 的 GIN 索引甚至能支撑高性能的包含查询这比扫全表高效得多。4. 多值字段与关联表用 EXISTS 才是正规军4.1 EXISTS 关联判断语义最清晰实际业务中最常见的判断一个字段是否包含某个数据其实是判断一条记录在关联表中是否存在对应记录。比如查询购买了商品 A 的用户本质就是在订单明细表里找有没有用户 商品A的组合。这种场景的标准写法是EXISTSSELECT u.* FROM users u WHERE EXISTS ( SELECT 1 FROM orders o WHERE o.user_id u.id AND o.product_id A );很多人初学者喜欢用IN子查询INNER JOIN后加DISTINCT但EXISTS有几个不可替代的优势语义直白不用去重不需要担心 JOIN 之后数据膨胀短路执行找到第一条匹配记录就会停止不需要扫描完整个子表NULL 处理更安全IN子查询一旦子结果集里出现NULL整条判断就会被置为未知而EXISTS不会受此影响IN和EXISTS在现代数据库优化器下性能差距已经不大但EXISTS的语义可读性和 NULL 安全性是全数据库通用的我建议把EXISTS作为首选。4.2 关联子查询与物化策略当包含的判断需要组合条件时比如用户至少有一个超过 1000 元的订单且订单状态为已完成SELECT u.* FROM users u WHERE EXISTS ( SELECT 1 FROM orders o WHERE o.user_id u.id AND o.amount 1000 AND o.status completed );此时如果orders表很大(user_id, status, amount)的联合索引就至关重要。我见过很多类似的慢查询根因都是 WHERE 条件里的列没有对应的索引导致 EXISTS 子查询内部又要全表扫。注意EXISTS 的性能不是天然就好子查询里的关联条件和过滤条件必须命中索引否则它就是一个隐形的全表扫描嵌套循环。4.3 反范式字段与冗余设计有时候性能要求极高比如首页推荐位要实时判断用户是否包含某个标签每次现查关联表扛不住这时候就需要冗余。常见做法是在主表上加一个tags字段把用户的所有标签 ID 用分隔符或 JSON 存进去查询时用FIND_IN_SET或JSON_CONTAINS快速判断。这种以空间换时间的做法牺牲了数据一致性换来了极低的查询延迟。但这个方案要接受一个事实冗余字段的更新时机需要你自己把控。标签变更时是同步更新主表字段还是异步任务批量刷新都得在设计阶段定好。不然很容易出现标签改了主表字段还是旧值的数据不一致问题。5. 各种方案的性能对比与选型建议5.1 索引利用情况一览判断方法能不能走索引直接决定它在生产环境的存活能力。我把常见场景整理成了下表场景写法示例能否走索引适用数据量级备注前缀匹配name LIKE 张%能如果排序规则合适千万级唯一能用索引的 LIKE 形式任意位置包含name LIKE %张%否万级以内全表扫描任意位置包含CHARINDEX(张, name) 0否万级以内无法索引正则匹配content REGEXP 模式否千级以内性能最差逗号列表精确匹配FIND_IN_SET(数码, tags)否万级以内MySQL 专属关联表存在判断EXISTS (SELECT 1 ...)能子查询条件命中索引亿级处理大规模数据的首选JSON 数组包含tags [vip]能GIN 索引千万级PostgreSQL 专属全文检索MATCH(...) AGAINST(...)能全文索引千万级适合长文本5.2 模糊查询优化方案假设你确实需要做长文本包含查询数据量又大到LIKE %xxx%扛不住有几个方案可以救场方案一全文索引。MySQL 的FULLTEXT索引、PostgreSQL 的tsvector、SQL Server 的全文索引都是为文本包含场景设计的。以 MySQL 为例-- 建全文索引 ALTER TABLE articles ADD FULLTEXT INDEX ft_content (content); -- 使用全文检索注意默认英文分词中文需要 ngram 解析器 SELECT * FROM articles WHERE MATCH(content) AGAINST(数据库优化 IN BOOLEAN MODE);中文场景下 MySQL 全文索引必须指定ngram解析器否则分词效果会很差ALTER TABLE articles ADD FULLTEXT INDEX ft_content (content) WITH PARSER ngram;方案二缓存层。对包含判断的结果做缓存比如用 Redis 维护标签 - 文章 ID 列表的倒排索引判断时先查缓存。这是互联网大厂处理海量标签筛选的通用做法。方案三外部搜索引擎。如果文本量大到数据库全文索引都吃力就该上 Elasticsearch 了。ES 的倒排索引和分词能力跟数据库的模糊查询不在一个量级。但引入 ES 也意味着架构复杂度提升不是小项目该碰的。5.3 适用场景选择最后我给一个直白的选型建议照着选基本不会错临时查一下、数据量小、不追求性能用LIKE %str%简单直接需要精确判断逗号分隔列表中的某一项MySQL 里用FIND_IN_SET字符串里要匹配复杂模式用正则数据量大、是关联关系用EXISTS 索引JSON 字段用各数据库官方的 JSON 包含函数高频模糊查询 大量文本直接考虑全文索引或搜索引擎6. 实战案例从需求到 SQL 的完整推演6.1 案例一根据标签筛选文章业务需求articles表的tags字段是逗号分隔的字符串比如python,数据库,算法需要筛出包含数据库标签的文章。新手写法SELECT * FROM articles WHERE tags LIKE %数据库%;问题LIKE会把数据库设计、数据库优化等都匹配出来而业务上要的可能仅仅是标签项精确等于数据库。正确写法SELECT * FROM articles WHERE FIND_IN_SET(数据库, tags) 0;如果tags的设计本来就是精确标签项这个需求用FIND_IN_SET才能保证语义正确。标签包含关系用LIKE在很多情况下是业务 bug 的温床。6.2 案例二查询包含指定商品的所有订单业务需求orders表的主键订单号是order_noorder_items表存订单明细order_no,product_id需要筛选包含P1001商品的所有订单。这个场景本质是一对多表的存在判断最优解是EXISTSSELECT * FROM orders o WHERE EXISTS ( SELECT 1 FROM order_items oi WHERE oi.order_no o.order_no AND oi.product_id P1001 );为了让查询更快在order_items上建(product_id, order_no)联合索引。这样 EXISTS 子查询能直接通过product_id定位再用order_no做关联回表扫描行数非常少。6.3 案例三JSON 字段中的动态属性筛选业务需求user_ext表有一个attributesJSON 字段里面存了各种动态扩展属性比如{vip_level: 3, channel: app}要筛选出渠道为app的用户。MySQL 8.0 的JSON_CONTAINS可以优雅地解决SELECT * FROM user_ext WHERE JSON_CONTAINS(attributes-$.channel, app);PostgreSQL 的 JSONB 写法更简洁SELECT * FROM user_ext WHERE attributes {channel: app};注意 JSON 字符串值在JSON_CONTAINS里要加上双引号的转义因为 JSON 的字符串值本身就是带引号的。这个细节我见过好几个人在这里折腾半天实则就是引号问题。7. 常见问题与避坑技巧7.1 通配符转义查找 % 开头的数据现象要查code字段里以%开头的记录直接写WHERE code LIKE %%%会把所有数据都查出来因为第一个%是通配符不是数据本身。正解用ESCAPE定义转义符SELECT * FROM products WHERE code LIKE \%% ESCAPE \;这里\%表示字面量的百分号后面的%%才是通配符任意字符。类似地_也需要转义。7.2 大小写敏感性包含判断时大小写不一致现象用户名字段存的是ZhangSan用WHERE name LIKE %zhangsan%查不到结果。原因PostgreSQL 的LIKE默认区分大小写MySQL 在默认排序规则utf8mb4_0900_ai_ci下不区分SQL Server 看字段排序规则。跨数据库行为不一致。解决思路-- PostgreSQL用 ILIKE 或 lower() SELECT * FROM users WHERE name ILIKE %zhangsan%; SELECT * FROM users WHERE LOWER(name) LIKE %zhangsan%; -- SQL Server可以显式指定排序规则或转换大小写 SELECT * FROM users WHERE LOWER(name) LIKE %zhangsan%;注意LOWER(name)会让索引失效如果这个字段查询频繁最好在写入时就统一存小写或者建函数索引。7.3 空值与 NULL 的边界情况现象字段值为NULL时WHERE tag_list LIKE %python%返回的是UNKNOWN也就是查不出来这不是我们预期的不包含。解决思路如果业务上需要把 NULL 也算作不包含要显式处理SELECT * FROM articles WHERE tag_list IS NULL OR FIND_IN_SET(python, tag_list) 0;反过来如果你要查包含 python的记录NULL值当然不会匹配到不用额外处理。但在做 NOT 判断时一定要意识到NOT LIKE会把NULL也排除掉这是很多统计对不上的真正原因。7.4 性能陷阱隐式转换与函数包裹现象字段是字符串类型但查询时传了数字数据库可能会做隐式类型转换导致索引失效。比如WHERE order_no LIKE %2024%结果集很大查询很慢。原因字符串字段上用LIKE本身就是全表扫描如果还要配合函数或隐式转换性能更堪忧。排查手段用EXPLAIN查看执行计划确认是不是走了ALL全表扫描有没有出现Using filesort之类的额外负担。慢查询日志常开的团队这时候基本就是定位到这条 SQL 优化。7.5 不同数据库函数迁移的坑同一份业务逻辑从 MySQL 迁到 PostgreSQLFIND_IN_SET就没有了从 SQL Server 迁到 MySQLCHARINDEX也没了。我的经验是核心业务 SQL 尽量使用 SQL 标准语法EXISTS、LIKE、子查询数据库专属函数封装在数据访问层这样将来换数据库改动集中在少数的数据库方言适配层而不是散落在整个项目里。8. 写在最后的经验沉淀判断字段是否包含某个数据看起来是小问题但越简单的需求往往越考验对数据库能力的理解深度。我踩过最深的坑就是用万能的LIKE %...%去硬刚所有包含场景结果数据量一上来查询慢到超时也见过别人用FIND_IN_SET处理 JSON 数组对着复杂结构一头雾水更常见的是明明用EXISTS几行就能优雅解决的关联包含问题偏偏要写成INNER JOIN DISTINCT性能差还难读。现在我的决策逻辑很简单先搞清楚包含的语义再选实现方式。是字符串子串包含、列表精确包含、关联存在判断还是 JSON 包含语义定了写法基本就定了。剩下的就是考虑数据量、索引和可维护性。另有一个小经验分享给读者数据库优化不是写完 SQL 才考虑的事而是在设计表结构时就要预判未来的查询模式。如果业务从一开始就定了文章要按标签筛选那就应该建多对多关联表加索引而不是图省事在单个字符串字段里堆逗号——后者只适合临时用不适合支撑一个持续迭代的线上业务。希望这篇文章能把SQL 包含判断这条线讲透。遇到具体问题欢迎在评论区里带上你的表结构和 SQL 一起讨论。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号