必须用json_table展开再聚合:直接对json字段count()或group by只统计整行数,而非数组元素数;需先用'$[*]'路径炸开成行,再按原始主键group by并聚合。

MySQL 8.0+ 必须用 JSON_TABLE 展开再聚合
直接对 JSON 字段用 COUNT() 或 GROUP BY 会统计整行数,不是数组元素数。必须先“炸开”成行,再聚合。
常见错误:把 JSON_LENGTH(items) 当作每单商品数——它只返回顶层长度,若字段是 {"items": [...]},结果就是 1(键个数),不是数组元素个数。
-
JSON_TABLE的路径必须写'$[*]'才能遍历全部元素;写'$'或'$.items'(没加[*])会失效 - 字段类型必须是 JSON:如果
items是VARCHAR存的字符串"[{...}]",得先CAST(items AS JSON),否则静默返回空 -
COLUMNS中定义的类型要和实际值匹配,比如qty INT PATH '$.qty',若 JSON 里"qty": "2"(字符串),会转成NULL - 加
WHERE JSON_VALID(items) AND JSON_LENGTH(items) > 0过滤非法或空数组,避免整行丢失
PostgreSQL 用 jsonb_array_elements() 更直接
它本质是 set-returning 函数,效果等价于 UNNEST,但只认 jsonb 类型。
典型报错:function jsonb_array_elements(text) does not exist——说明字段是 TEXT 或 JSON,没转 jsonb。
- 强制转换:写成
jsonb_array_elements(items::jsonb),但确保内容合法,否则报invalid input syntax for type jsonb - 安全过滤:加
WHERE jsonb_typeof(items) = 'array',避免对象、字符串、NULL导致报错或跳行 - 提取字段用
->>(字符串)或->+ 类型转换,例如(elem->>'qty')::int - 展开后必须按原始主键
GROUP BY orders.id,否则聚合跨行混算
SQL Server 用 OPENJSON 要显式声明模式
OPENJSON 默认只返回键值对(key, value, type),没法直接 GROUP BY 业务字段,必须配 WITH 子句。
常见问题:写了 SELECT * FROM OPENJSON(@json) 却没加 WITH,结果里只有 value 字符串,无法区分 id 和 qty。
- 路径写法:主数组展开必须用
'$[*]',不能省略[*];若数据在{"data": [...]}里,得写'$.data[*]' -
WITH里字段名和类型要一一对应,例如id INT '$.id', qty INT '$.qty' - 原始字段是
NVARCHAR(MAX)时,确保内容是合法 JSON,否则OPENJSON返回空且无提示 - 聚合前建议套一层 CTE,避免重复解析同一 JSON 字段
聚合前别漏掉原始主键 GROUP BY
展开后行数膨胀,比如一个订单含 3 个商品,就变成 3 行。此时若直接 GROUP BY item_id,会把所有订单的同款商品合并统计——这不是“每单总件数”,而是“全量销量”。
真正要的是“每个订单的总数量”或“每单去重商品数”,就必须保留原始记录标识。
- 外层查询必须
GROUP BY orders.id(或其他主键),再套SUM(qty)、COUNT(DISTINCT item_id) - 若还需关联原始表其他字段(如
order_time),要么加进GROUP BY,要么用MIN(order_time)等聚合包裹 - MySQL 不支持
ANY_VALUE()以外的非分组字段裸露,PostgreSQL 允许但需明确语义 - 展开函数本身不参与分组逻辑——
jsonb_array_elements()和JSON_TABLE都只是生成中间行集,聚合必须在外层显式写











