jsonb_array_elements()将JSONB数组每个元素展开为一行,仅支持jsonb类型,需显式转换;遇NULL返回空结果集,建议配合LATERAL LEFT JOIN保留原行,展开后默认别名value应显式重命名。

PostgreSQL 中用 jsonb_array_elements() 展开 JSON 数组
PostgreSQL 的 jsonb_array_elements() 是解包 JSON 数组最直接的函数,它把每个数组元素转成一行。注意:它只接受 jsonb 类型,如果字段是 json,得先显式转换:jsonb_array_elements(data::jsonb)。
常见错误是直接对 text 字段调用该函数,会报错 function jsonb_array_elements(text) does not exist;还有人误用 json_array_elements()(不带 b),在含 Unicode 或数字精度要求高的场景可能丢数据,优先选 jsonb_* 系列。
- 必须确保目标字段非 NULL,否则
jsonb_array_elements()返回空结果集(不是 NULL 行) - 若想保留原始行即使数组为空,需改用
LEFT JOIN LATERAL jsonb_array_elements(...) - 展开后字段默认别名为
value,建议显式重命名,比如jsonb_array_elements(tags::jsonb) AS tag
MySQL 8.0+ 用 JSON_TABLE() 实现等效解包
MySQL 没有类似 jsonb_array_elements() 的标量函数,必须用 JSON_TABLE() —— 它本质是把 JSON 数组“虚拟建表”,需要指定列定义和路径表达式。
典型写法:JSON_TABLE(data, '$[*]' COLUMNS (name TEXT PATH '$.name', score INT PATH '$.score')) AS jt。其中 '$[*]' 表示遍历根数组所有元素,COLUMNS 定义输出字段名、类型和取值路径。
- 路径表达式必须用单引号包裹,且不能省略
$开头;写成'[*]'会报错 -
TEXT类型字段实际最大长度受 MySQL 行限制影响,若原始 JSON 字符串超长,可能被截断,可改用MEDIUMTEXT并在COLUMNS中声明 -
JSON_TABLE()不支持对 NULL 或非法 JSON 值静默跳过,遇到会报错Invalid JSON text,需提前用JSON_VALID()过滤
解包后分组聚合的常见陷阱
解包只是第一步,真正容易出问题的是后续 GROUP BY 和聚合逻辑。核心原则:所有非聚合字段必须出现在 GROUP BY 中,且不能直接 GROUP BY 原始 JSON 字段(它不可哈希/不可比较)。
- PostgreSQL 示例:要统计每个标签出现次数,得写
SELECT tag->>'name' AS tag_name, COUNT(*) FROM tbl, jsonb_array_elements(tags::jsonb) AS tag GROUP BY tag->>'name';写成GROUP BY tag会失败 - MySQL 示例:需在
JSON_TABLE()外层再套一层查询做聚合,不能在JSON_TABLE()的COLUMNS子句里写聚合函数 - 性能隐患:对大表反复展开 JSON 数组可能触发全表扫描 + 大量中间行,建议在 JSON 字段上建 GIN 索引(PostgreSQL)或生成列 + 普通索引(MySQL)加速过滤
SQLite 3.38+ 的替代方案:json_each() 和 json_tree()
SQLite 虽无原生数组解包聚合语法,但 3.38 版本起支持 json_each(),可将 JSON 数组转为含 key、value、type 等列的虚拟表。
关键点:json_each() 默认从根开始解析,若 JSON 是数组,value 列即为各元素;若 JSON 是对象,则需用 json_tree() 配合 WHERE type = 'array' 定位后再展开。
-
json_each()对非数组 JSON(如对象或标量)返回空结果,不会报错,但容易漏数据——务必确认字段内容结构 - SQLite 不支持在
GROUP BY中直接用json_each().value,需用子查询或 CTE 先展开再聚合 - 没有 JSON 索引支持,纯内存解析,千万级数据慎用
JSON 数组解包本身不难,难的是跨数据库语法差异大、错误反馈模糊、性能边界不直观。最常被忽略的是:没验证 JSON 数据一致性(空数组、NULL 值、嵌套深度超标),结果聚合时静默丢失行或报错中断。


















