从 jsonb(第 12 页)获取多个 key/value 对

Get multiple key/value pairs from jsonb (pg 12)

Postgres 12

table_1:
  id int,
  attributes jsonb,
  layer_id int -- foreign key
table_2:
  id int,
  labels jsonb, -- this is an array, and its elements correspond to some of the keys in table_1.attributes
  layer_id int -- foreign key

layer_id = layer_id 上加入这些表后,我只想 select table_1.attributes 中与 table_2.labels[= 中的键匹配的 key/value 对16=]

例如,如果我们有 table_1 行,例如:

attributes: { "a": 1, "b": 3, "c": "hello" },
layer_id: 5

和 table_2 行如:

labels: '["a", "c"]',
layer_id: 5

my objective 将得到包含 { "a": 1, "c": "hello" }

的列

编辑

通过以下方法我们可以获得所需的对象w/o 为每个标签获取一行(在处理数百万行时这可能会让人望而却步)

  1. 获取所需标签的数组
  2. 从属性对象中删除这些键
  3. 获取剩余的钥匙
  4. 从原始属性对象中删除那些,留下对应于所需标签的 key/values,例如,
with table_1 (layer_id, attributes) as (
  values (5, '{"a": 1, "b": 3, "c": "hello"}'::jsonb),
         (7, '{"d": 0, "e": 9, "f": "bye"}'::jsonb)
), table_2 (layer_id, labels) as (
  values (5, '["a", "c"]'::jsonb),
         (7, '[]'::jsonb)
)
  select t1.layer_id,
         a.attributes result
    from table_1 t1
         join table_2 t2 on t2.layer_id = t1.layer_id
         cross join lateral (
             SELECT t1.attributes - ARRAY (
                    SELECT 
                        jsonb_object_keys(attributes - ARRAY (
                            SELECT
                                jsonb_array_elements_text(labels)))) AS attributes) a
   group by t1.layer_id, a.attributes

 layer_id |         result
----------+------------------------
        5 | {"a": 1, "c": "hello"}
        7 | []
(2 rows)

原始解决方案

使用 jsonb_array_elements() 扩展,然后使用 jsonb_object_agg() 聚合:

with table_1 as (
  select 5 as layer_id, '{"a": 1, "b": 3, "c": "hello"}'::jsonb as attributes
), table_2 as (
  select 5 as layer_id, '["a", "c"]'::jsonb as labels
)
select t2.layer_id, jsonb_object_agg(l.label, t1.attributes->l.label) 
  from table_2 t2 
       cross join lateral jsonb_array_elements_text(t2.labels) as l(label)
       join table_1 t1 on t1.layer_id = t2.layer_id
 group by t2.layer_id;

 layer_id |    jsonb_object_agg    
----------+------------------------
        5 | {"a": 1, "c": "hello"}
(1 row)

要处理空 labels 元素,请尝试以下方法。由于 jsonb_object_agg() 不接受 null 作为标签,所以我无法弄清楚如何在不将原始查询放入 CTE 的情况下使其工作。

with table_1 (layer_id, attributes) as (
  values (5, '{"a": 1, "b": 3, "c": "hello"}'::jsonb),
         (7, '{"d": 0, "e": 9, "f": "bye"}'::jsonb)
), table_2 (layer_id, labels) as (
  values (5, '["a", "c"]'::jsonb),
         (7, '[]'::jsonb)
), expand as (
  select t1.layer_id,
         jsonb_object_agg(l.label, t1.attributes->l.label) result
    from table_1 t1
         join table_2 t2 on t2.layer_id = t1.layer_id
         cross join lateral jsonb_array_elements_text(t2.labels) as l(label)
   group by t1.layer_id
)
select t1.layer_id, coalesce(x.result, '[]'::jsonb) as result
  from table_1 t1
       left join expand x
              on x.layer_id = t1.layer_id
;

 layer_id |         result
----------+------------------------
        5 | {"a": 1, "c": "hello"}
        7 | []
(2 rows)