将JSONB数组转换为列

时间:2017-02-17 23:03:45

标签: postgresql jsonb postgresql-9.5

我有一个返回包含jsonb对象的单个列的查询。以下是一些返回行的示例(此处为jsonb::text的易读性):

[{"amount": 3, "consumable": "Mitsu 90mm ODEX Pilot Bit", "consumed_date": "2017-02-17"}]
[{"amount": 3, "consumable": "BOP Rubber", "consumed_date": "2017-02-17"}]
[{"amount": 13, "consumable": "NWJ Long Saver Sub Pin x Box", "consumed_date": "2017-02-18"}, 
    {"amount": 70, "consumable": "NWJ Long Saver Sub Pin x Box", "consumed_date": "2017-02-17"}]
[{"amount": 63, "consumable": "Methyl Hydrate - Per Litre", "consumed_date": "2017-02-17"}]
[{"amount": 6, "consumable": "Cyclone Discharge Hose Assembly", "consumed_date": "2017-02-17"}]

给出这些示例数据,我想要一个查询来生成一个具有模式的表

consumable                      | 2017-02-17 | 2017-02-18
`````````````````````````````````````````````````````````
BOP Rubber                      | 3          | NULL
Cyclone Discharge Hose Assembly | 6          | NULL
Methyl Hydrate - Per Litre      | 63         | NULL
Mitsu 90mm ODEX Pilot Bit       | 3          | NULL
NWJ Long Saver Sub Pin x Box    | 70         | 13

无法将tablefunc扩展名添加到此数据库实例。

1 个答案:

答案 0 :(得分:2)

第1步。

您可以为每个consumable在json对象中汇总金额。 jsons以consumed_dates为键:

with test(js) as (
values
    ('[{"amount": 3, "consumable": "Mitsu 90mm ODEX Pilot Bit", "consumed_date": "2017-02-17"}]'::jsonb),
    ('[{"amount": 3, "consumable": "BOP Rubber", "consumed_date": "2017-02-17"}]'),
    ('[{"amount": 13, "consumable": "NWJ Long Saver Sub Pin x Box", "consumed_date": "2017-02-18"}, {"amount": 70, "consumable": "NWJ Long Saver Sub Pin x Box", "consumed_date": "2017-02-17"}]'),
    ('[{"amount": 63, "consumable": "Methyl Hydrate - Per Litre", "consumed_date": "2017-02-17"}]'),
    ('[{"amount": 6, "consumable": "Cyclone Discharge Hose Assembly", "consumed_date": "2017-02-17"}]')
)

select consumable, jsonb_object_agg(consumed_date, amount) as amounts
from (
    select
        e->>'consumable' as consumable,
        (e->>'amount')::int as amount,
        e->>'consumed_date' as consumed_date
    from test,
    lateral jsonb_array_elements(js) e
    ) s
group by 1;

           consumable            |               amounts                
---------------------------------+--------------------------------------
 Mitsu 90mm ODEX Pilot Bit       | {"2017-02-17": 3}
 BOP Rubber                      | {"2017-02-17": 3}
 NWJ Long Saver Sub Pin x Box    | {"2017-02-17": 70, "2017-02-18": 13}
 Methyl Hydrate - Per Litre      | {"2017-02-17": 63}
 Cyclone Discharge Hose Assembly | {"2017-02-17": 6}
(5 rows)

第2步。

使用in this post所述的函数create_jsonb_pivot_view()。 您必须使用上述数据创建一个新表来使用该功能,例如:

drop table if exists my_new_table;
create table my_new_table as
    with test(js) as (
    --
    -- the above query
    ---
    group by 1;

select create_jsonb_pivot_view('my_new_table', 'consumable', 'amounts');

select * 
from my_new_table_view
order by 1;

           consumable            | 2017-02-17 | 2017-02-18 
---------------------------------+------------+------------
 BOP Rubber                      | 3          | 
 Cyclone Discharge Hose Assembly | 6          | 
 Methyl Hydrate - Per Litre      | 63         | 
 Mitsu 90mm ODEX Pilot Bit       | 3          | 
 NWJ Long Saver Sub Pin x Box    | 70         | 13
(5 rows)    

替代解决方案

您可以使用此查询在不使用DDL的情况下尽可能接近所需格式的结果(假设the_data包含问题中的数据):

with aux as (
    select
        e->>'consumable' as consumable,
        e->>'amount' as amount,
        e->>'consumed_date' as consumed_date
    from the_data,
    lateral jsonb_array_elements(js) e
)
select 
    null as consumable, 
    array_agg(distinct consumed_date order by consumed_date) as amounts
from aux
union all
select 
    consumable, 
    array_agg(amount order by consumed_date) as amounts
from (
    select distinct t1.consumed_date, t2.consumable
    from aux t1
    cross join aux t2
    ) s
left join aux t using(consumed_date, consumable)
group by 1
order by 1 nulls first;

           consumable            |         amounts         
---------------------------------+-------------------------
                                 | {2017-02-17,2017-02-18}
 BOP Rubber                      | {3,NULL}
 Cyclone Discharge Hose Assembly | {6,NULL}
 Methyl Hydrate - Per Litre      | {63,NULL}
 Mitsu 90mm ODEX Pilot Bit       | {3,NULL}
 NWJ Long Saver Sub Pin x Box    | {70,13}
(6 rows)