从EAV结构结果集中选择JSON值

时间:2019-01-18 07:05:06

标签: postgresql jsonb entity-attribute-value

给出一个EAV结构的结果集,例如:

id   | attributeName  | stringValue | intValue  | BooleanValue
---------------------------------------------------------------
1       stringFoo            v1
1       stringFooList        v2   
1       stringFooList        v3
1       intFoo                           10
1       intFooList                       10
1       intFooList                       20
1       booleanFoo                                     true
1       booleanFooList                                 true 
1       booleanFooList                                 true

如何将所有属性和值对选择为JSON / JSONB格式的单个值,就像这样:

{
    "stringFoo"         : "v1" , 
    "stringFooList"     : ["v2","v3"] ,
    "intFoo"            : 10 ,
    "intFooList"        : [10,20],
    "booleanFoo"        : true,
    "booleanFooList"    : [true,true]
}

如果某个属性(例如stringFooList)具有多个属性值,它将把它格式化为JSON数组。

我正在使用PostgreSQL 9.6

2 个答案:

答案 0 :(得分:1)

您可以执行以下操作:

select id, jsonb_object_agg(att, value)
from (
  select id, 
         attributename as att, 
         case 
           when count(*) > 1 then 
               jsonb_agg(coalesce(stringvalue,intvalue::text,booleanvalue::text)) 
           else 
              to_jsonb(min(coalesce(stringvalue,intvalue::text,booleanvalue::text)))
         end as value
  from eav
  group by id, attributename
) t
group by id;

内部选择将多个值聚合到一个JSON数组中,将单个值聚合到JSON标量值中。然后,外部查询将为所有行构建一个JSON值。

在线示例:https://rextester.com/TLCRN79815

答案 1 :(得分:0)

@a_horse_with_no_name的回答给了我一个很好的开始。我扩展了他/她的答案,并提出了以下查询,以使JSON数组中的元素具有与PostgreSQL中定义的数据类型相同的数据类型。

select id, jsonb_object_agg(att, 
    case 
      when strval is not null then strval
      when intvalue is not null then intvalue
      else boolVal
     end 
    )
from (
  select id, 
         attributename as att,  
         case when count(*) > 1 then 
            jsonb_agg(stringvalue) filter (where stringvalue is not null) 
         else 
            to_jsonb(min(stringvalue) filter (where stringvalue is not null))     
         end as strVal, 

         case when count(*) > 1 then 
            jsonb_agg(intvalue) filter (where intvalue is not null) 
         else 
            to_jsonb(min(intvalue) filter (where intvalue is not null))     
         end as intvalue, 

         case when count(*) > 1 then 
            jsonb_agg(booleanvalue) filter (where booleanvalue is not null) 
         else 
            to_jsonb(bool_and(booleanvalue) filter (where booleanvalue is not null))     
         end as boolVal
  from eav
  group by id, attributename
) t
group by id;