如何在Postgres中使用jsonb列进行groupBy以模拟EAV计数表?

huangapple go评论57阅读模式
英文:

How to groupBy in postgres with jsonb column to mimic an EAV count table?

问题

我有一个类似这样的jsonb列:

Id Data
1 {state: ["CA", "NY"], county:["Los Angeles"]}
2 {city: ["Kansas City"], zipCode: "12345"}
3 {state: ["CO, WA"], zipCode: "5212"}

但我以前的数据结构是这样的:

Id Attribute Value
1 state CA
1 state NY
2 city Kansas City

等等...

以前我只需要编写像这样的简单查询:

SELECT attribute, value, count(*)
    FROM table
    GROUP BY attribute, value;

输出结果将会是:

Attribute Value Count
county New York County 11
city Kansas City 22
state CA 15
zip 100010 21
state NY 5

我正在尝试生成与上面相同的表格,但使用jsonb表格时我遇到了问题。

我已经尝试使用jsonb_each_text,如下所示:

with t1 as
    (select jsonb_each_text(facets) as rec from document_template_facets)
select (rec).key, sum((rec).value::int) from t1 group by (rec).key;

问题是它不适用于我的数据中的数组类型,如城市、县等等... 有没有办法在上面的查询中将数组展平以使计数工作?

英文:

I have a jsonb column that looks like this:

Id Data
1 {state: ["CA", "NY"], county:["Los Angeles"]}
2 {city: ["Kansas City"], zipCode: "12345"}
3 {state: ["CO, WA"], zipCode: "5212"}

But I used to have a data structure like so:

Id Attribute Value
1 state CA
1 state NY
2 city Kansas City

etc...

I used to just be able to write a simple query like this:

SELECT attribute, value, count(*)
    FROM table
    GROUP BY attribute, value;

and the output would yield:

Attribute Value Count
county New York County 11
city Kansas City 22
state CA 15
zip 100010 21
state NY 5

I'm trying to generate the same table above but with the jsonb table but I'm having trouble getting the desired output.

I've tried using jsonb_each_text like so:

with t1 as
    (select jsonb_each_text(facets) as rec from document_template_facets)
select (rec).key, sum((rec).value::int) from t1 group by (rec).key;

The problem is that it doesn't work for array types in my data like city, county, etc... Any way to get the arrays to be flattened in the query above to get the count to work?

答案1

得分: 2

jsonb_each_text() 函数返回行,因此必须位于查询的 from 部分。可以使用 cross join lateral 完成这一操作。

以下查询返回您想要的结果。jsonb_array_elements_text 内的 case 处理数据中的标量值,如 zipCode 元素,将其转换为单元素数组:

with expand_keys as (
  select id, k, a
    from tab
         cross join lateral jsonb_each(data) as j(k, a)
), expand_arrays as (
  select id, k, a, el
    from expand_keys
         cross join lateral 
           jsonb_array_elements_text(
             case jsonb_typeof(a)
               when 'array' then a
               else jsonb_build_array(a)
             end
           ) as ar(el)
)
-- select * from expand_arrays; --运行此行来查看中间结果
select k as attribute, el as value, count(*) as cnt
  from expand_arrays
 group by k, el;

Fiddle 链接

英文:

The jsonb_each_text() function returns rows, so it has to be in the from part of your query. Do that with a cross join lateral.

The below query returns what you want. The case within the jsonb_array_elements_text handles scalar values like the zipCode elements in your data by turning those into single-element arrays:

with expand_keys as (
  select id, k, a
    from tab
         cross join lateral jsonb_each(data) as j(k, a)
), expand_arrays as (
  select id, k, a, el
    from expand_keys
         cross join lateral 
           jsonb_array_elements_text(
             case jsonb_typeof(a)
               when 'array' then a
               else jsonb_build_array(a)
             end
           ) as ar(el)
)
-- select * from expand_arrays; --run this, instead, to see interim results
select k as attribute, el as value, count(*) as cnt
  from expand_arrays
 group by k, el;

Fiddle here

huangapple
  • 本文由 发表于 2023年2月10日 09:54:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/75406238.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定