无法从 JSON_ARRAY_ELEMENTS() 获取所有值

Unable to get all the values from JSON_ARRAY_ELEMENTS()

Table 样本数据:

create table tbl_jsdata
(
id int,
p_id int,
field_name text,
field_value text 
);

insert into tbl_jsdata values       
(1,101,'Name','Sam'),
(2,101,'City','Dubai'),
(3,101,'Pin','1235'),
(4,101,'Country','UAE'),
(5,102,'Name','Sam'),
(6,102,'City','Dubai'),
(7,102,'Name','Sam Jack'),
(8,102,'Name','Test'),
(9,102,'Name',null);

json_agg查询:

drop table if exists tempJSData;

select p_id,
    json_build_array(json_object_agg(field_name, field_value)) into tempJSData
from tbl_jsdata
group by p_id;

得到结果:

select p_id,(json_array_elements(json_build_array)->>'Name')::text Namess
from tempJSData


p_id    Namess
---------------------------------
101     Sam
102

预期结果:

p_id    Namess
---------------------------------
101     Sam
102     Sam
102     Sam Jack
102     Test
102

我认为这是因为您没有创建 Name.

的数组

如果您检查您的查询

select p_id,
    json_build_array(json_object_agg(field_name, field_value))
from tbl_jsdata
group by p_id;

结果是

 p_id |                                      json_build_array
------+---------------------------------------------------------------------------------------------
  101 | [{ "Name" : "Sam", "City" : "Dubai", "Pin" : "1235", "Country" : "UAE" }]
  102 | [{ "Name" : "Sam", "City" : "Dubai", "Name" : "Sam Jack", "Name" : "Test", "Name" : null }]
(2 rows)

Name 字段有多个相邻条目。以下 json_array_elements(json_build_array)->>'Name' 将仅获取第一个条目。我建议首先创建一个基于 p_idfield_name 的数组

with array_built as (
    select p_id,field_name,
        array_agg(field_value) field_value
    from tbl_jsdata
    group by p_id, field_name
)
select p_id,
    jsonb_object_agg(field_name, field_value)
from array_built
group by p_id
;

结果可以优化,因为它创建一个数组,即使只有一个值

 p_id |                             jsonb_object_agg
------+---------------------------------------------------------------------------
  101 | {"Pin": ["1235"], "City": ["Dubai"], "Name": ["Sam"], "Country": ["UAE"]}
  102 | {"City": ["Dubai"], "Name": ["Sam", "Sam Jack", "Test", null]}
(2 rows)

但现在您可以正确解析它,整个查询是

select p_id,
    json_build_array(json_object_agg(field_name, field_value)) 
from tbl_jsdata
group by p_id;

select p_id,
    json_build_array(json_object_agg(field_name, field_value)) 
from tbl_jsdata
group by p_id;

with array_built as (
    select p_id,field_name,
        array_agg(field_value) field_value
    from tbl_jsdata
    group by p_id, field_name
), agg as (
select p_id,
    jsonb_object_agg(field_name, field_value) json_doc
from array_built
group by p_id
)

select p_id, jsonb_array_elements(json_doc->'Name')  from agg;
;

预期结果为

 p_id | jsonb_array_elements
------+----------------------
  101 | "Sam"
  102 | "Sam"
  102 | "Sam Jack"
  102 | "Test"
  102 | null
(5 rows)

您可以使用 json_each_text 来提取数组的值,并在 WHERE 子句中仅过滤您想要的 key:

SELECT p_id,j.value
FROM tempJSData, json_each_text(json_build_array->0) j
WHERE j.key = 'Name';

 p_id |  value   
------+----------
  101 | Sam
  102 | Sam
  102 | Sam Jack
  102 | Test
  102 | 
(5 rows)

注意:此查询假定您的 json 格式是最终格式。如果不是,请考虑创建一个 Name 数组而不是一个包含名称的对象数组:name[foo,bar] 而不是 [name:foo,name:bar]。 Ftisiot 的回答提出了一个很好的观点。

演示:db<>fiddle

您的 JSON 聚合本质上是无效的,因为您正在创建一个 JSON 值,其中同一个键出现不止一次。如果您使用了推荐的 jsonb 数据类型,则重复键将被删除。

我认为这种聚合更有意义:

create table tempjsdata
as
select p_id,
       jsonb_agg(jsonb_build_object(field_name, field_value)) as names
from tbl_jsdata
group by p_id

以上生成以下结果:

p_id | names                                                                                       
-----+---------------------------------------------------------------------------------------------
 101 | [{"Name": "Sam"}, {"City": "Dubai"}, {"Pin": "1235"}, {"Country": "UAE"}]                   
 102 | [{"Name": "Sam"}, {"City": "Dubai"}, {"Name": "Sam Jack"}, {"Name": "Test"}, {"Name": null}]

那么你可以使用:

select p_id, 
       x.*
from tempjsdata
  cross join lateral (
     select x.item ->> 'Name'
     from jsonb_array_elements(t.names) as x(item)  
     where x.item ? 'Name'
  ) x

Online example