位置:首页 > SQL > SQL 如何对 JSON 字段进行分组聚合

SQL 如何对 JSON 字段进行分组聚合

时间:2026-08-24  |  作者:骑光打字机  |  阅读:0

JSON字段无法直接进行GROUP BY操作,必须先将其展开成行,然后再进行分组。在MySQL 8.0+中,可以使用JSON_TABLE函数来展开;在PostgreSQL中,则可以使用jsonb_array_elements()函数配合LATERAL。不过,在使用这两种方法时,都需要确保类型匹配、进行空值过滤,并选择正确的分组维度。

SQL如何对JSON字段进行分组聚合

直接回答:JSON字段不能直接GROUP BY,必须先展开成行,再按原始主键或提取字段分组;否则聚合会跨记录混算,结果完全不可信。

MySQL 8.0+ 用 JSON_TABLE 展开后 GROUP BY

与PostgreSQL不同,MySQL中并没有类似的set-returning函数,在这种情况下,JSON_TABLE()就成了唯一可靠的选择。它的作用是将JSON数组“虚拟建表”,但需要注意的是,必须明确地声明路径和列结构。

  • 路径写 '$[*]' 才能遍历全部元素,写 '$[0]' 或省略路径会漏数据
  • COLUMNS 中类型必须匹配实际值,比如数组元素是字符串就用 TEXT PATH '$',是数字就用 INT PATH '$.qty'
  • 字段存的是字符串(如 "["a","b"]")而非 JSON 类型,JSON_TABLE() 会静默返回空——得先 CAST(items AS JSON) 或确保 DDL 定义为 JSON
  • NULL 或非法 JSON 默认跳过整行,加 LEFT JOIN + COALESCE 更稳妥

示例(统计每个订单里各商品数量总和):

SELECT o.id, jt.item_id, SUM(jt.qty) AS total_qty
FROM orders o
LEFT JOIN JSON_TABLE(
o.items,
'$[*]' COLUMNS (
item_id INT PATH '$.id',
qty INT PATH '$.qty'
)
) AS jt ON TRUE
WHERE JSON_VALID(o.items) AND JSON_LENGTH(o.items) > 0
GROUP BY o.id, jt.item_id;

PostgreSQL 用 jsonb_array_elements() 配合 LATERAL

jsonb_array_elements() 是最常用方式,但它只接受 jsonb 类型,且对 NULL 或空数组无容忍——不提前过滤会导致整行丢失。

  • 字段是 json 类型?必须先转:items::jsonb
  • 空数组 []NULL 都要过滤:WHERE items IS NOT NULL AND jsonb_typeof(items) = 'array' AND jsonb_array_length(items) > 0
  • 提取字段用 ->>'key'(字符串)或 (elem->>'qty')::int(转数值),别漏类型转换
  • 展开后必须保留原始主键(如 orders.id)用于外层 GROUP BY,否则聚合跨订单

示例(统计每单中各状态出现次数):

SELECT o.id, elem->>'status' AS status, COUNT(*) AS cnt
FROM orders o, LATERAL jsonb_array_elements(o.items) AS elem
WHERE o.items IS NOT NULL
AND jsonb_typeof(o.items) = 'array'
AND jsonb_array_length(o.items) > 0
GROUP BY o.id, elem->>'status';

聚合前必须确认:你到底想按什么分组?

这是最容易被跳过的逻辑起点。JSON 字段展开后,行数膨胀,分组维度错位会导致结果彻底失真。

  • 想“按订单汇总”?GROUP BY orders.id —— 这是绝大多数场景的正确选择
  • 想“按商品 ID 统计全表销量”?那就要 GROUP BY elem->>'id',但得确保所有订单都展开且没丢行
  • 原始记录还有其他关键字段(如 order_timeuser_id)?它们要么进 GROUP BY,要么用 MIN()/ANY_VALUE() 包裹,否则 PG 会报错
  • MySQL 中若需保留非聚合字段又不想写满 GROUP BY,得开 sql_mode 允许 ONLY_FULL_GROUP_BY 以外的行为,但不推荐

真正麻烦的不是函数怎么写,而是展开后数据与业务语义的对齐——比如一个订单里两个相同商品对象,是该去重还是累加?这得看业务规则,SQL 本身不会替你判断。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多