如何对聚合后生成的jsonb对象数组设置过滤条件?
实现方案
你遇到的核心问题是分组维度新增set_id后会拆分聚合结果,下面提供两种可直接落地的方案,按优先级排序:
方案1:先筛选目标卡片再聚合(性能最优,推荐)
不需要改动原有聚合逻辑,仅新增一层筛选逻辑找出所有存在set_id='123'的卡片ID,再关联原有聚合结果即可,能直接复用printings表set_id字段的B树索引,性能最优。
SELECT cards.*, COUNT(cards.*) OVER() AS full_count, p.printing_information FROM cards -- 筛选出所有至少有一条printing符合set_id='123'的卡片 INNER JOIN ( SELECT DISTINCT pr.card_id FROM printings pr WHERE pr.set_id = '123' ) valid_cards ON cards.card_id = valid_cards.card_id -- 原有聚合逻辑不变,返回对应卡片的全量printing信息 LEFT JOIN ( SELECT pr.card_id, jsonb_agg(to_jsonb(pr)) AS printing_information FROM printings pr GROUP BY pr.card_id ) p ON cards.card_id = p.card_id
如果需要简化写法,也可以把筛选逻辑合并到聚合子查询中:
SELECT cards.*, COUNT(cards.*) OVER() AS full_count, p.printing_information FROM cards LEFT JOIN ( SELECT pr.card_id, jsonb_agg(to_jsonb(pr)) AS printing_information, -- 新增字段标记该卡片是否存在目标set_id的printing bool_or(pr.set_id = '123') AS has_target_set FROM printings pr GROUP BY pr.card_id ) p ON cards.card_id = p.card_id WHERE p.has_target_set = true
方案2:直接对聚合后的jsonb数组做包含查询
如果你不想改动原有子查询的聚合逻辑,可以直接用PostgreSQL的jsonb包含操作符实现你要的过滤效果,写法非常简洁:
WHERE p.printing_information @> '[{"set_id": "123"}]'::jsonb
该语法会判断printing_information数组中是否存在至少一个元素包含set_id='123'的属性,完全符合你理想中的查询逻辑。如果你的printing_information字段已经创建了GIN索引,该查询的性能也能满足大多数场景。
场景补充说明
如果你的需求是「仅保留聚合数组中set_id='123'的printing项,不需要返回卡片的全量printing信息」,直接在聚合子查询中加WHERE条件即可:
SELECT cards.*, COUNT(cards.*) OVER() AS full_count, p.printing_information FROM cards LEFT JOIN (SELECT pr.card_id, jsonb_agg(to_jsonb(pr)) AS printing_information FROM printings pr WHERE pr.set_id = '123' -- 新增过滤条件 GROUP BY pr.card_id) p ON cards.card_id = p.card_id WHERE p.printing_information IS NOT NULL
内容的提问来源于stack exchange,提问作者Jake Alsemgeest
相关产品推荐
相关产品推荐

