MySQL JSON 数组查询实战:JSON_CONTAINS 用法详解与 LIKE 的本质区别
MySQL 中如何优雅地查询 JSON 数组字段(以及它和 LIKE 的区别)
很多业务表里都会有这么一个字段:
json[{"uri":"/v1/xxx"}]
它是一个 JSON 数组,数组里是对象。
问题来了:
如果我想查出「数组中存在 uri = '/v1/xxx'」的记录,该怎么写 SQL?
再进一步:
这样写和
LIKE有什么区别?
这篇文章把这个问题讲清楚。
一、推荐写法:JSON_CONTAINS
假设表结构:
- 表名:
api_table - 字段名:
data - 类型:
JSON(推荐)或VARCHAR
查询语句:
sqlSELECT * FROM api_table WHERE JSON_CONTAINS( data, JSON_OBJECT('uri', '/v1/xxx') );
这句 SQL 做了两件事。
1️⃣ JSON_OBJECT 是什么?
sqlJSON_OBJECT('uri', '/v1/xxx')
它会生成:
json{"uri":"/v1/xxx"}
也就是说,它只是帮你构造一个 JSON 对象,而不是手写 JSON 字符串。
这样可以避免转义错误,也更规范。
2️⃣ JSON_CONTAINS 是什么?
sqlJSON_CONTAINS(target, candidate)
意思是:
判断 target 这个 JSON 文档里,是否包含 candidate。
在我们的场景里,它实际在做类似这样的判断:
pseudofor item in data_array: if item.uri == "/v1/xxx": return true
只要数组里有一个对象满足条件,就会匹配成功。
二、它的匹配规则是什么?
这是理解它的关键。
假设数据是:
json[{"uri":"/v1/xxx","method":"GET"}]
你查询:
sqlJSON_CONTAINS(data, JSON_OBJECT('uri','/v1/xxx'))
仍然会匹配成功。
因为:
json{"uri":"/v1/xxx","method":"GET"}
包含了:
json{"uri":"/v1/xxx"}
它做的是“结构包含”,不是字符串比较。
三、为什么不直接用 LIKE?
很多人会写:
sqlWHERE data LIKE '%"/v1/xxx"%'
看起来简单直接,但问题不少。
❌ 问题一:误匹配
如果数据是:
json[{"uri":"/v1/xxxx"}]
也会被匹配到。
因为 LIKE 只是字符串匹配。
❌ 问题二:依赖格式
JSON 里如果有空格:
json{"uri" : "/v1/xxx"}
或者 key 顺序变化,匹配行为可能出现不可预期的结果。
❌ 问题三:语义不清晰
看到 LIKE '%xxx%',读代码的人无法一眼判断:
- 是模糊查询?
- 是结构查询?
- 是精确匹配?
而 JSON_CONTAINS 非常明确:
在 JSON 结构里找对象。
四、两者本质区别
| 对比项 | JSON_CONTAINS | LIKE |
|---|---|---|
| 匹配方式 | 结构匹配 | 字符串匹配 |
| 是否严谨 | 高 | 低 |
| 是否容易误匹配 | 不容易 | 容易 |
| 可读性 | 强 | 弱 |
简单说:
LIKE 是“文本层面的解决方案”。
JSON_CONTAINS 是“数据结构层面的解决方案”。
如果字段本身是 JSON,就应该用 JSON 的方式去查询它。
五、什么时候适合这样做?
这种写法适合:
- 配置类字段
- 半结构化数据
- 不频繁过滤的场景
如果这个字段是核心查询条件,并且数据量很大,那可能需要重新考虑数据结构设计,而不是只纠结 SQL 写法。
六、总结
如果你只记住一句话:
当字段是 JSON,就用 JSON 函数查询,而不是用 LIKE 拼字符串。
JSON_OBJECT 用来构造 JSON。
JSON_CONTAINS 用来判断结构是否存在。
它的优势不在于“更快”,
而在于“更准确、更规范、更可维护”。
数据库查询,能用结构表达,就不要退回字符串匹配。
这往往是代码质量的分水岭。