必须先将数组展开为多行再分组,否则聚合错乱或报错;PostgreSQL用jsonb_array_elements()需jsonb类型并过滤空/NULL;MySQL用JSON_TABLE()须校验JSON有效性及路径正确性;ClickHouse用ARRAY JOIN语法而非arrayJoin()函数。

直接展开数组后分组,必须先把它“炸成多行”,再按原始主键或目标字段 GROUP BY;否则聚合会跨行错乱,或者报错。
PostgreSQL 用 jsonb_array_elements() 展开 JSON 数组再分组
这个函数只接受 jsonb 类型,传 json 或字符串会报 function jsonb_array_elements(text) does not exist。空数组或 NULL 字段会导致整行丢失(LATERAL 关联无结果),得提前过滤。
- 安全写法:加
WHERE jsonb_typeof(items) = 'array' AND items IS NOT NULL - 展开后别直接
GROUP BY elem——它返回的是jsonb值,得先取字段,比如elem->>'id'或(elem->>'qty')::int - 若要统计每单总数量,外层必须再套一层
GROUP BY orders.id,不能把SUM写在LATERAL里 - 性能差时可建生成列:
ALTER TABLE orders ADD COLUMN item_count INT GENERATED ALWAYS AS (jsonb_array_length(items)) STORED
MySQL 8.0+ 必须用 JSON_TABLE(),不能靠 JSON_EXTRACT 或 COUNT
JSON_LENGTH() 看似能数元素,但它只对顶层是数组的 JSON 有效;如果字段存的是 {"tags": ["a","b"]},JSON_LENGTH() 返回 1(对象键个数),不是 tags 里元素数。
- 路径必须写
'$[*]',写'$'或'$.tags[*]'(除非字段名真是tags)会漏数据或报错 -
COLUMNS里字段类型要和实际值匹配:JSON 里存"25"却定义为INT,该行对应字段变成0,导致分组错乱 - 必须加
WHERE JSON_VALID(items) AND JSON_LENGTH(items) > 0,否则非法 JSON 或空数组会让JSON_TABLE静默跳过整行 - 高频固定路径查询(如总是按
$.variant_id分组),建议建虚拟列 + 索引,避免每次解析
ClickHouse 用 ARRAY JOIN,别误用 arrayJoin() 直接 GROUP BY
arrayJoin(tags) 是表函数,不是列,不能直接出现在 GROUP BY 后——ClickHouse 会报 Column 'tags' is not under aggregate function and not in GROUP BY。
- 正确写法是
FROM events ARRAY JOIN tags AS tag GROUP BY tag,ARRAY JOIN是语法子句,不是函数调用 - 空数组或
NULL默认被丢弃;需要保留,改用LEFT ARRAY JOIN,再配合ifNull(tag, 'N/A') - 原行带
['a','a','b'],默认展开为 3 行,count()中a就是 2 次;要去重统计“含 a 的事件数”,得先arrayDistinct(tags)再ARRAY JOIN - 数据膨胀严重(100 万行 × 平均 10 个元素 = 1000 万行),务必
WHERE过滤后再ARRAY JOIN,别反过来
最容易被忽略的是:所有数据库中,“展开”和“分组”是两个独立阶段,中间不能省略显式行化;任何试图在展开前就聚合、或把展开结果当标量直接分组的操作,都会出错或语义错误。

















