展平来自 JSONB 字段的聚合键/值对?

Posted

技术标签:

【中文标题】展平来自 JSONB 字段的聚合键/值对?【英文标题】:Flatten aggregated key/value pairs from a JSONB field? 【发布时间】:2016-05-12 17:05:28 【问题描述】:

我在 Postgres 9.4 中使用下表:

     Column      │         Type         │ Modifiers
─────────────────┼──────────────────────┼──────────────────────
 id              │ integer              │ not null default
 practice_id     │ character varying(6) │ not null
 date            │ date                 │ not null
 pct_id          │ character varying(3) │
 total_list_size │ double precision     │ not null
 star_pu         │ jsonb                │

我有以下疑问:

SELECT date,
       AVG(total_list_size) AS total_list_size,
       json_object_agg(key, val) AS star_pu
FROM (SELECT date,
             SUM(total_list_size) AS total_list_size,
             key, SUM(value::numeric) val FROM frontend_practicelist p,
             jsonb_each_text(star_pu)
       GROUP BY date, key ) p
GROUP BY date
ORDER BY date;

它为我提供了一个附加到star_pu 的 JSON 对象的结果:

date            │ 2013-04-01
total_list_size │ 56025123.000000000000
star_pu         │  "antidepressants_cost" : 180102416.8036909901975399, "antiepileptic_drugs_cost" : 296228344.171576079922216... 

相反,我想将 JSON 结果展平为一系列命名空间键,因此结果如下所示:

date                             │ 2013-04-01
total_list_size                  │ 56025123.000000000000
star_pu.antidepressants_cost     │ 180102416.8036909901975399
star_pu.antiepileptic_drugs_cost │ 296228344.171576079922216 
...

这可能吗?

【问题讨论】:

这可能吗? 没有明确定义你的输出列。即使crosstab 也需要这样做。 【参考方案1】:

这个特殊情况

下面的函数根据表格动态创建视图:

create or replace function create_totals_view(table_name text)
returns void language plpgsql as $$
declare
    s text;
begin
    execute format ($fmt$
        select string_agg(format('star_pu->>''%s'' "%s"', key, key), ',')
        from (
            select distinct key
            from %s, json_each(star_pu)
            order by 1
            ) s;
        $fmt$, '%s', '%s', table_name)
    into s;
    execute format('
        drop view if exists %s_view;
        create view %s_view as 
        select date, total_list_size, %s from %s', 
        table_name, table_name, s, table_name);
end $$;

首先,根据您的查询创建一个表。

create table totals as

    SELECT date,
           AVG(total_list_size) AS total_list_size,
           json_object_agg(key, val) AS star_pu
    FROM (SELECT date,
                 SUM(total_list_size) AS total_list_size,
                 key, SUM(value::numeric) val FROM frontend_practicelist p,
                 jsonb_each_text(star_pu)
           GROUP BY date, key ) p
    GROUP BY date
    ORDER BY date;

接下来,使用该函数,该函数将创建一个以带有_view 后缀的表命名的视图:

select create_totals_view('totals');

最后查询视图:

select * from totals_view;

广义解决方案(针对jsonb)

create or replace function create_jsonb_flat_view
    (table_name text, regular_columns text, json_column text)
    returns text language plpgsql as $$
declare
    cols text;
begin
    execute format ($ex$
        select string_agg(format('%2$s->>%%1$L "%%1$s"', key), ', ')
        from (
            select distinct key
            from %1$s, jsonb_each(%2$s)
            order by 1
            ) s;
        $ex$, table_name, json_column)
    into cols;
    execute format($ex$
        drop view if exists %1$s_view;
        create view %1$s_view as 
        select %2$s, %3$s from %1$s
        $ex$, table_name, regular_columns, cols);
    return cols;
end $$;

用法:

create table example (id int, name text, params jsonb);
insert into example values
(1, 'Anna', '"height": 175, "weight": 55'),
(2, 'Bob', '"age": 22, "height": 188'),
(3, 'Cindy', '"age": 25, "weight": 48, "pretty": true');

select create_jsonb_flat_view('example', 'id, name', 'params');

select * from example_view;

 id | name  | age | height | pretty | weight 
----+-------+-----+--------+--------+--------
  1 | Anna  |     | 175    |        | 55
  2 | Bob   | 22  | 188    |        | 
  3 | Cindy | 25  |        | true   | 48
(3 rows)

【讨论】:

这是迄今为止我见过的关于堆栈溢出的最有用和最深刻的答案。将 jsonb 字段展平为像这样的视图的一般功能应该是基于此处概述的技术的 postgresql 的一部分,因为它解决了 jsonb 字段出现的各种问题。 这太棒了!有没有一种简单的方法可以使这个工作与多个 jsonb 列一起工作?例如。 select create_jsonb_flat_view('example', 'id, name', 'params1, params2');? @adilapapaya - 一个简单的方法:select create_jsonb_flat_view('example', 'id, name', 'params1 || params2'); 难怪你刚刚发现了那个副本,我可能也记得这个。 有没有办法扩展它以使对象数组变平?

以上是关于展平来自 JSONB 字段的聚合键/值对?的主要内容,如果未能解决你的问题,请参考以下文章

从结合 SQL 组的 JSONB 数组中聚合不同的值

用于聚合键包含数字的键值对的算法

Postgres Jsonb 聚合

MongoDB聚合:将数组属性展平为根数组

在子选择中创建聚合,而无需将引用的字段添加到分组列表

通过 jsonb 列中的给定键/值对过滤行