JSON_CONTAINS_PATH(data, 'one', '$.tags')是检查JSON键是否存在的正确方法,它返回1或0,支持嵌套路径和无效JSON容错,语义清晰且适合建索引。

JSON_CONTAINS 适合查数组里的值,但不适用于“是否存在某个键”
直接用 JSON_CONTAINS 判断字段是否含某个键(比如 "tags"),会失败——它只认 JSON 值或文档结构,不识别键名。你传 '"tags"' 进去,它会在数组里找字符串 "tags",而不是检查对象里有没有 tags 这个 key。
真正该用的是 JSON_EXTRACT 配合 IS NOT NULL,或者更轻量的 -> 操作符:
-
WHERE data->'$.tags' IS NOT NULL:适用于 JSON 对象,检查tags键是否存在且非 null -
WHERE JSON_EXTRACT(data, '$.tags') IS NOT NULL:语义等价,但稍冗余,->更推荐 - 注意:
->返回带引号的 JSON 值(如"hello"),->>返回去引号字符串(如hello),但判断存在性时二者效果一样
JSON_CONTAINS_PATH 才是专为“键是否存在”设计的函数
MySQL 5.7.8+ 提供了 JSON_CONTAINS_PATH,它明确区分三种模式:'one'、'all'、'strict'(后者仅 8.0+ 支持)。日常查“某个键存不存在”,用 'one' 最稳妥:
-
WHERE JSON_CONTAINS_PATH(data, 'one', '$.tags'):返回 1 表示存在,0 表示不存在或路径非法 - 支持嵌套路径,比如
'$.specs.cpu'或'$[0].name'(对数组首项对象查name) - 比
JSON_EXTRACT(...) IS NOT NULL更安全:即使data是无效 JSON 或NULL,它也返回 0 而不是报错或 NULL - 性能上无本质优势,但语义清晰,可读性强,推荐新代码优先用
查 JSON 数组里是否含某 key 的对象,得先定位再提取
如果字段存的是 JSON 数组(如 [{"id":1,"type":"A"},{"id":2,"type":"B"}]),而你想查“数组中至少有一个对象含有 type 键”,就不能直接 ->'$.type' ——那会返回 NULL,因为根节点是数组,不是对象。
正确做法分两步:
- 用
JSON_LENGTH确保是数组:JSON_TYPE(data) = 'ARRAY'(可选,增强健壮性) - 用
JSON_CONTAINS_PATH配合通配符路径:JSON_CONTAINS_PATH(data, 'one', '$[*].type')——$[*]表示遍历所有数组元素 - 若需兼容老版本(JSON_SEARCH(data, 'one', 'dummy', NULL, '$[*].type'),只要返回非 NULL 就说明路径可达(虽略绕,但有效)
别忽略索引和性能陷阱
JSON_CONTAINS_PATH 和 -> 都无法直接走 B-Tree 索引,全表扫描风险高。真要高频查某个 key 是否存在,必须建生成列 + 索引:
- 添加虚拟列:
ALTER TABLE t ADD COLUMN has_tags TINYINT AS (JSON_CONTAINS_PATH(data, 'one', '$.tags')) STORED - 在该列建索引:
CREATE INDEX idx_has_tags ON t(has_tags) - 查询就变成:
WHERE has_tags = 1,能命中索引 - 注意:生成列不能基于函数返回值为
NULL的表达式(如JSON_EXTRACT在 key 不存在时返回 NULL),所以优先用JSON_CONTAINS_PATH这类确定返回 0/1 的函数
最易被忽略的一点:JSON 字段本身不索引,任何 WHERE 里带函数调用(包括 ->)都大概率触发全表扫描,除非你主动把判定逻辑下沉到生成列里。别指望优化器自动帮你“猜”出该走什么索引。


















