如何根据对象id将jsonb数组展开到每个jsonb列的对象中?

时间:2017-09-07 21:34:18

标签: sql postgresql

给定类似于以下内容的现有数据结构:

CREATE TEMP TABLE sample (id int, metadata_array jsonb, text_id_one jsonb, text_id_two jsonb);
INSERT INTO sample
VALUES ('1', '[{"id": "textIdOne", "data": "foo"},{"id": "textIdTwo", "data": "bar"}]'), ('2', '[{"id": "textIdOne", "data": "baz"},{"id": "textIdTwo", "data": "fiz"}]');

我正在尝试将现有元数据列中的jsonb对象数组展开到同一个表中的新jsonb列中;我已经基于已知的固定ID键列表textIdOnetextIdTwo等创建了我。

我以为我接近使用jsonb_populate_recordset()但后来意识到将按照所有jsonb对象的键填充列;不是我想要的。期望的结果是基于对象id的每列对象。

此操作中唯一另一个棘手的部分是我的JSON对象的id值使用camelCase并且似​​乎应该避免引用/引用的列名,但我不介意引用或修改列名称作为结束的手段&更新查询完成后,我可以根据需要手动更改列名。

我正在使用PostgreSQL 9.5.2

现有数据&结构:

id | metadata_array jsonb                             | text_id_one jsonb | text_id_two jsonb
---------------------------------------------------------------------------------------------
1  | [{"id": "textIdOne"...}, {"id": "textIdTwo"...}] | NULL              | NULL
2  | [{"id": "textIdOne"...}, {"id": "textIdTwo"...}] | NULL              | NULL

期望的结果:

id | metadata_array jsonb     | text_id_one jsonb      | text_id_two jsonb
-------------------------------------------------------------------------------
1  | [{"id": "textIdOne",...  | {"id": "textIdOne"...} | {"id": "textIdTwo"...}
2  | [{"id": "textIdOne",...  | {"id": "textIdOne"...} | {"id": "textIdTwo"...}

澄清:

感谢大家到目前为止的答案!虽然我知道完整的密钥列表(大约9个)但我不能指望顺序是一致的。

2 个答案:

答案 0 :(得分:1)

如果所有json数组都包含两个新列的两个元素,那么使用固定路径,如dmfay的答案。否则,你应该使用jsonb_array_elements()两次取消数组,分别对text_id_onetext_id_two取消。

update sample t set
    text_id_one = value1,
    text_id_two = value2
from sample s, 
    jsonb_array_elements(s.metadata_array) as e1(value1),
    jsonb_array_elements(s.metadata_array) as e2(value2)
where s.id = t.id
    and value1->>'id' = 'textIdOne'
    and value2->>'id' = 'textIdTwo'
returning t.*

Test the query in SqlFiddle.

如果数组中有两个以上的元素,这个变体可能更有效(也更方便):

update sample t
set 
    text_id_one = arr1->0,
    text_id_two = arr2->0
from (
    select 
        id,
        jsonb_agg(value) filter (where value->>'id' = 'textIdOne') as arr1,
        jsonb_agg(value) filter (where value->>'id' = 'textIdTwo') as arr2
    from sample,
    jsonb_array_elements(metadata_array)
    group by id
    ) s
where t.id = s.id
returning t.*

SqlFiddle.

答案 1 :(得分:0)

你说id列表是“固定的”; metadata_array中对象的排序是否一致?你可以通过简单的遍历来做到这一点:

UPDATE sample
   SET text_id_one = metadata_array->0,
       text_id_two = metadata_array->1;