【问题标题】:How to unwind jsonb array into object per jsonb column based on object id?如何根据对象 id 将 jsonb 数组展开为每个 jsonb 列的对象?
【发布时间】:2017-09-07 21:34:18
【问题描述】:

给定一个类似于以下的现有数据结构:

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 个),但我不能指望顺序一致。

【问题讨论】:

    标签: sql postgresql


    【解决方案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.

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

      • 我不能指望顺序一致;我已经用这个澄清更新了这个问题:-)
      猜你喜欢
      • 1970-01-01
      • 2016-11-25
      • 1970-01-01
      • 2020-12-09
      • 1970-01-01
      • 1970-01-01
      • 2022-01-11
      • 1970-01-01
      • 2020-03-16
      相关资源
      最近更新 更多