不能直接 group by jsonb 字段,因其为复合类型,group by 仅支持标量;需用 jsonb_array_elements_text() 或 jsonb_array_elements() 配合 lateral 展开后分组。

不能直接对 JSONB 字段 GROUP BY,必须先用 jsonb_array_elements() 或 jsonb_array_elements_text() 展开成行,再配合 LATERAL 关联原表才能正确分组统计。
为什么不能直接 GROUP BY jsonb 字段?
JSONB 是复合类型,GROUP BY 只接受标量(如 text、int),而 data->'tags' 返回的是 jsonb 类型。即使值看起来一样(如 ["a","b"]),PostgreSQL 也会按完整结构比对,导致本该合并的组被拆开。常见错误写法:GROUP BY data->'tags' 或 GROUP BY data->>'tags'(后者会报错,因 ->> 对数组返回 NULL)。
怎么安全展开 JSONB 数组并分组?
核心是两步:用 jsonb_array_elements_text() 提取字符串元素,再用 LATERAL 绑定到原行。漏掉 LATERAL 会导致语法错误或笛卡尔积。
基于三引擎设计,从微信文章、新闻和博客网页提取干净内容,支持标题作者日期元数据,多格式和批量处理。
- 数组存纯字符串(如
["apple","banana"]):优先用jsonb_array_elements_text(t.data->'tags'),它直接返回text,无需类型转换 - 数组存对象(如
[{"name":"x"},{"name":"y"}]):用jsonb_array_elements(t.data->'tags'),再通过->>'name'提取字段 - 必须写成
FROM items t, LATERAL jsonb_array_elements_text(t.data->'tags') AS e(tag),其中e(tag)显式声明别名和列名 - 若原字段可能为
NULL或空数组,jsonb_array_elements_text()默认跳过——这通常符合预期;如需保留空行,得改用LEFT JOIN LATERAL ... ON true并配合COALESCE
分组时容易忽略的 NULL 和重复问题
解构后的 e.tag 可能含 NULL(原 JSONB 里有 null 字面量)或重复值(如 ["a","a","b"])。直接 GROUP BY e.tag 会让 NULL 单独成组,且 COUNT(*) 会把同一行的多个相同 tag 算多次。
- 过滤
NULL:加WHERE e.tag IS NOT NULL - 统计“含某 tag 的记录数”(去重):用
COUNT(DISTINCT t.id) - 统计“tag 出现总次数”(含重复):用
COUNT(*),但得清楚这是频次而非记录数 - 性能关键:在
data->'tags'上建 GIN 索引,CREATE INDEX ON items USING GIN ((data->'tags'));
真正麻烦的不是函数调用本身,而是展开后行数爆炸带来的性能抖动和 NULL 处理逻辑——这两点不提前想清楚,查出来数据对不上,还得回溯重写。










