怎样用 SQL JSON 函数查询数组中的指定元素

叶初
叶初
发布时间:2026-08-24 | 150人浏览过
原创

前言

很多人第一次查 SQL 里的 JSON 数组,都会遇到同一个现象:语句看起来没错,结果却始终查不到。问题通常不在函数本身,而在字段是不是原生 JSON 类型、路径是否明确指向数组元素,以及查询值的类型有没有写对。 这篇文章按“为什么查不到、怎么取指定元素、批量筛选时该怎么选、不同数据库写法有什么差异”四个问题展开。读完后,你可以判断什么时候继续用 JSON_CONTAINS,什么时候该换成 JSON_TABLE 或 PostgreSQL 的 jsonb 操作符。

很多人第一次查 SQL 里的 JSON 数组,都会遇到同一个现象:语句看起来没错,结果却始终查不到。问题通常不在函数本身,而在字段是不是原生 JSON 类型、路径是否明确指向数组元素,以及查询值的类型有没有写对。

这篇文章按“为什么查不到、怎么取指定元素、批量筛选时该怎么选、不同数据库写法有什么差异”四个问题展开。读完后,你可以判断什么时候继续用 JSON_CONTAINS,什么时候该换成 JSON_TABLE 或 PostgreSQL 的 jsonb 操作符。

MySQL 里 JSON_CONTAINS 为什么查不到数组值

JSON_CONTAINS 在 MySQL 里查 JSON 数组时,经常被误以为“函数不稳定”。实际更常见的原因只有两个:字段并不是 JSON 类型,而是用 VARCHAR 或 TEXT 存了一段字符串;或者路径参数漏掉了 $[*],导致函数只在顶层结构匹配,不会自动遍历数组元素。

MySQL 中 JSON_CONTAINS 查询数组失败原因与正确写法示意图
JSON_CONTAINS 数组查询排查图用字段类型、路径写法和查询值类型三项检查,快速定位 JSON_CONTAINS。

先检查字段类型和路径写法

  • 字段必须是 JSON 类型,而不是 VARCHAR 或 TEXT;否则 JSON_CONTAINS 可能返回 NULL,条件无法成立。
  • 查询数组中是否包含某个值时,要显式指定数组路径:JSON_CONTAINS(json_col, '"value"', '$[*]')
  • $[*] 的作用是匹配数组里的每一个元素;如果省略,函数不会帮你遍历数组。

字符串和数字的写法不能混用

JSON 查询对类型匹配比较严格。字符串值和数字值的写法不同,不能指望数据库自动帮你做隐式转换。

  • 字符串要保留 JSON 字符串格式:JSON_CONTAINS(json_col, '"value"', '$[*]')
  • 数字不要再包一层引号,例如:JSON_CONTAINS(json_col, 42, '$[*]')

如果数组里实际存的是 [1,"2",true] 这种混合类型,查询结果更容易出现“看起来相同,实际匹配不上”的情况。

想取数组第 N 个元素,MySQL 应该怎么写

如果你的需求不是“数组里有没有某个值”,而是“拿到第几个元素”,更适合用 JSON_EXTRACT。它支持标准数组下标访问,但有两个行为要先记住:MySQL 数组下标从 0 开始;访问越界时返回 NULL,不会报错。

按下标读取数组元素

  • 取第一个元素:JSON_EXTRACT(data, '$[0]')
  • 取最后一个元素,需要配合 JSON_LENGTH 计算下标:JSON_EXTRACT(data, CONCAT('$[', JSON_LENGTH(data) - 1, ']'))

结果格式和空值处理要提前考虑

JSON_EXTRACT 返回的是 JSON 值本身,因此字符串结果会带双引号,数字和布尔值则不会。如果你需要普通文本结果,可以再套一层 JSON_UNQUOTE()。

  • 去掉字符串结果的双引号:JSON_UNQUOTE(JSON_EXTRACT(data, '$[0]'))
  • 字段为空、不是数组或下标越界时,结果通常是 NULL
  • 在业务查询里,建议用 COALESCE 做兜底,避免后续逻辑直接吃到空值

筛选包含某个值的记录时,别只盯着 JSON_CONTAINS

当需求变成“找出所有数组里包含某个标签的记录”时,JSON_CONTAINS 虽然能写,但它更适合小规模、临时性过滤。原因是这种写法通常性能一般,也难以利用索引。

MySQL 中 JSON_EXTRACT 与 JSON_TABLE 的使用场景对比图
MySQL 数组读取与筛选选型图当需求从单个元素读取转向批量筛选时,JSON_EXTRACT、JSON_CONTAINS 与。

JSON_CONTAINS 适合简单判断,JSON_TABLE 更适合可扩展查询

最直接的写法是这样:

WHERE JSON_CONTAINS(tags, '"urgent"', '$[*]')

它的优点是语句短、上手快,缺点是更容易走全表扫描。对于数据量较大的表,MySQL 8.0+ 更稳妥的方案通常是把数组展开成行,再按普通列过滤。

SELECT t.* FROM orders t,
JSON_TABLE(t.tags, '$[*]' COLUMNS(tag VARCHAR(20) PATH '$')) AS jt
WHERE jt.tag = 'urgent'

这种方式的核心思路,是先把 tags 数组拆成多行 tag,再进行标准筛选。如果再结合生成列与索引,查询可维护性和性能都会更好一些。

使用 JSON_TABLE 前要先做合法性校验

JSON_TABLE 有一个很实际的前提:输入必须是合法 JSON 数组。空值、损坏 JSON 或非数组内容,都可能直接报错。

  • 使用前建议确认字段内容是合法 JSON
  • 必要时可增加 JSON_VALID(t.tags) 作为安全校验

PostgreSQL 查询 JSONB 数组更直接

如果你用的是 PostgreSQL,处理 JSON 数组通常会比 MySQL 顺手一些。原因是 jsonb 提供了更直接的操作符,不必频繁手写路径表达式。

PostgreSQL jsonb 数组查询操作符与适用对象示意图
PostgreSQL jsonb 数组操作图PostgreSQL 的 jsonb 查询更偏向操作符风格。

常用 jsonb 操作符和写法

  • 查数组是否包含某个字符串:tags @> '["urgent"]'::jsonb
  • 查对象数组中是否包含某种结构:items @> '[{"type":"error"}]'::jsonb
  • 把数组元素展开成行:SELECT jsonb_array_elements(tags) FROM logs
  • 按对象字段过滤数组元素:WHERE tags @? '$[*] ? (@.status == "failed")'

在表达“数组里是否包含某个对象”或“对象数组里某字段是否满足条件”时,PostgreSQL 的 jsonb 语法通常更紧凑,也更容易读懂。

最后要注意的 3 类常见坑

无论是 MySQL 还是 PostgreSQL,真正容易出问题的都不是函数名,而是数据本身。

最常见的失配来源

  • 数组深度不一致:你以为查的是一维数组,实际字段里可能嵌套了更深一层结构。
  • 空值和非法内容:空字段、非数组 JSON、损坏 JSON,都会让提取或展开结果变成 NULL,甚至直接报错。
  • 类型混杂:例如 [1,"2",true] 这样的数组,字符串 "2" 和数字 2 并不等价;MySQL 不会自动帮你转换,PostgreSQL 也要求严格匹配。

实际写查询时,可以先确认三件事:字段是不是原生 JSON 类型、路径是否真的指向数组元素、查询值的类型是否和数组内容完全一致。大多数“明明有值却查不到”的问题,最后都能落到这三个检查点上。

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表