PostgreSQL:如何对 JSONB 字段中的所有属性求和?

问题描述 投票:0回答:4

我正在使用 Postgres 9.4。我有一个 JSONB 字段:

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

我可以很好地查询 JSONB 字段的原始值:

SELECT star_pu FROM mytable limit 1;
star_pu │ {"statins_cost": 16790.692924903742, "hypnotics_adq": 18523.58385328709, "laxatives_cost": 8456.98405165182, "analgesics_cost": 48271.21822239242, "oral_nsaids_cost": 9911.336052088493, "antidepressants_adq": 186715.7, "antidepressants_cost": 26885.54622478343, "bronchodilators_cost": 26646.54899847902, "cox-2_inhibitors_cost": 2063.4652015406728, "antiplatelet_drugs_cost": 4844.798321177439, "drugs_for_dementia_cost": 3390.569564110721, "antiepileptic_drugs_cost": 44990.94756286502, "oral_antibacterials_cost": 21047.048353859234, "oral_antibacterials_item": 5096.6501798218205, "ulcer_healing_drugs_cost": 15999.05326260261, "lipid-regulating_drugs_cost": 24711.589440943662, "proton_pump_inhibitors_cost": 14545.398978447573, "inhaled_corticosteroids_cost": 50759.91062192373, "calcium-channel_blockers_cost": 11571.457036131978, "omega-3_fatty_acid_compounds_adq": 2026.0, "benzodiazepine_caps_and_tabs_cost": 1800.2581325567717, "bisphosphonates_and_other_drugs_cost": 2996.912924744617, "drugs_acting_on_benzodiazepine_receptors_cost": 2993.142806352308, "drugs_affecting_the_renin_angiotensin_system_cost": 20255.500615282508, "drugs_used_in_parkinsonism_and_related_disorders_cost": 9812.457888596877}

现在我想要

SUM
整个表中的 JSONB 值,但我不知道该怎么做。理想情况下,我会取回一本字典,其中的键如上,值是求和值。

我可以对

SUM
一个 JSONB 字段明确执行以下操作:

    SELECT date, SUM(total_list_size) as total_list_size, 
    SUM((star_pu->>'oral_antibacterials_item')::float) AS star_pu_oral_antibac_items
    FROM mytable GROUP BY date ORDER BY date

但是我如何计算 JSONB 字段中所有属性的总和 - 最好将整个字段作为字典返回?理想情况下我会得到类似的东西:

star_pu │ {"statins_cost": very-large-number, "hypnotics_adq": very-large-number, ...

我想我可以通过明确地对每个键求和来手动获取每个字段,但我拥有 JSONB 字段的全部原因是有很多键,它们可能会改变。

可以安全地假设 JSONB 字段仅包含键和值,即深度为 1。

json postgresql postgresql-9.4 jsonb
4个回答
11
投票

查询应该完成的工作:

select date, json_object_agg(key, val)
from (
    select date, key, sum(value::numeric) val
    from mytable t, jsonb_each_text(star_pu)
    group by date, key
    ) s
group by date;

生成的 json 值将按键的字母顺序排序(

json_object_agg ()
的副作用)。我不知道这是否重要。


6
投票

我写了一个 Postgres 扩展 正是这样做的。安装完成后,您可以执行以下操作:

SELECT jsonb_deep_sum(star_pu) FROM mytable;

200 万行的基准在 4 秒内,@klin 的答案需要 11 秒


1
投票

可能有更好的方法,但至少这个有效:

WITH
  keys AS (SELECT DISTINCT jsonb_object_keys(star_pu) AS key FROM mytable),
  sums AS (SELECT key, sum((star_pu->>key)::float) AS total FROM keys, mytable GROUP BY key)
  SELECT json_object(array_agg(key), array_agg(total::text))::jsonb FROM sums

基本上它将 jsonbs 分解成行,从中获取名称,将它们相加,聚合成数组并创建一个 jsonb 结构。不幸的是没有

jsonb_object()
函数所以我们必须把它变成json然后转换成jsonb。


0
投票

如果需要在 PostgreSQL 中对 JSONB 列中的值求和,可以使用自定义聚合函数。我写了一个函数,就是这样做的。

create function custom_jsonb_add(a jsonb, b jsonb) returns jsonb as $$
    with expoded as (select *
                from jsonb_each(a) as t(key, value)
                union all
                select *
                from jsonb_each(b) as t(key, value)),
         folded as (select key, sum(value::numeric) as value
                from expoded
                group by key)
    select jsonb_object_agg(key, value)
    from folded
$$ language SQL immutable strict;

create aggregate custom_jsonb_add_agg(jsonb) (
    sfunc = custom_jsonb_add,
    stype = jsonb,
    initcond = '{}'
);
© www.soinside.com 2019 - 2024. All rights reserved.