Select JSON 来自 EAV 结构结果集的值

Select JSON value from a EAV structure result set

给定 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

如何 select 将所有属性和值对作为 JSON/JSONB 格式的单个值,类似于:

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

如果一个属性有多个属性值,例如 stringFooList ,它会将其格式化为 JSON 数组。

我正在使用 PostgreSQL 9.6

你可以这样做:

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;

内部 select 将多个值聚合成一个 JSON 数组,将单个值聚合成 JSON 标量值。然后外部查询构建所有行的单个 JSON 值。

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

@a_horse_with_no_name 的回答给了我一个好的开始。我扩展 his/her 答案并提出以下查询,以便 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;