mysql 8.0+、postgresql 和 sql server 均需先将 json 数组展开为行集才能正确分组统计:mysql 必用 json_table 配 '$[*]' 路径并严格匹配类型;postgresql 推荐 jsonb_array_elements() + lateral,配合 jsonb_typeof() 过滤和 left join 保空;sql server 需 openjson 配 with 显式定义模式,路径必须为 '$[*]'。

MySQL 8.0+ 必须用 JSON_TABLE 展开数组再分组
直接 COUNT(json_col) 或 GROUP BY json_col->'$.key' 不会按数组元素分组,只会把整个 JSON 当作一个值处理。必须先用 JSON_TABLE 把数组“炸开”成行,再 GROUP BY 展开后的字段。
-
JSON_TABLE的路径必须写'$[*]'(遍历全部元素),写'$'或'$.items[0]'会漏数据 - 字段类型要严格匹配:如果 JSON 里
"id": "123"是字符串,COLUMNS (id INT PATH '$.id')会静默转成0或丢弃整行 - 如果原始字段是
VARCHAR而非JSON类型,得先CAST(col AS JSON),否则JSON_TABLE返回空结果且无报错 - 加
WHERE JSON_VALID(col) AND JSON_LENGTH(col) > 0过滤非法或空 JSON,避免展开失败导致行丢失
PostgreSQL 用 jsonb_array_elements() + LATERAL 安全展开
jsonb_array_elements() 是最常用方式,但它只接受 jsonb 类型,且对 NULL 或非数组输入会直接报错或跳过整行——不是你漏写了 WHERE,而是函数本身行为如此。
Miller (mlr) 是一个命令行工具,用于查询、整形和重新格式化名称索引数据,如 CSV、TSV、JSON 和 JSON Lines。它将 awk、sed、cut、join 和 sort 的功能整合到一个专为结构化数据处理而构建的单一工具中。
- 强制转换用
col::jsonb,但确保内容合法,否则报invalid input syntax for type jsonb - 安全过滤写法:
WHERE jsonb_typeof(col) = 'array',比IS NOT NULL更准,能排除对象、字符串等干扰类型 - 展开后提取字段要用
elem->>'key'(字符串)或(elem->>'key')::int(数字),不能直接elem.key - 若需保留原记录即使数组为空,改用
LEFT JOIN LATERAL jsonb_array_elements(col::jsonb) AS elem ON true
别在 GROUP BY 里直接写 JSON 提取表达式
比如 GROUP BY params->>'$.item_id' 看似简洁,实际风险极高:一旦该路径不存在、返回 NULL 或类型不一致,分组结果就不可靠,且无法和展开后聚合对齐。
- 展开后的分组必须基于
JSON_TABLE或LATERAL生成的别名字段,如jt.item_id或elem->>'id' - 原始主键(如
orders.id)必须出现在外层GROUP BY中,否则聚合会跨记录混算 - 如果要统计每条记录的数组长度,优先用
JSON_LENGTH(col)(MySQL)或jsonb_array_length(col)(PG),而不是展开再COUNT(*)——前者快一个数量级
SQL Server 用 OPENJSON 需显式声明模式
OPENJSON 默认只返回键值对结构(key, value, type),没法直接 GROUP BY value;必须配合 WITH 子句定义列结构,否则聚合字段全是 NULL。
- 路径必须写
'$[*]'才能展开数组,写'$.items'只取第一层对象,不进数组内部 -
WITH里字段类型要和 JSON 实际值一致,比如id INT '$.id',若 JSON 里是字符串"id": "1",得写id NVARCHAR(32) '$.id'再转 - 原始字段是
NVARCHAR(MAX)?先确认内容是合法 JSON,否则OPENJSON返回空集且无提示 - 聚合前建议塞进 CTE,避免重复解析大 JSON 字段
GROUP BY json_col->'$.x',等于在赌数据结构永远不变——而现实里,字段偶尔为 NULL、偶尔是对象、偶尔是字符串,一炸就全乱。










