我的问题与此问题有些类似:
How to join jsonb array elements in Postgres?
但是我需要填写一些嵌套数组。为了保持简单,我只有一个表:
CREATE table tester(
id int,
name text,
d jsonb
)
INSERT INTO tester(id, name, d) VALUES
('1', 'bob', '[
{
"employees": [{"id":2},{"id":3},{"id":4}],
"coworkers": [{"id":5},{"id":6}]
},
{
"employees": [{"id":3},{"id":4}],
"coworkers": [{"id":5}]
}
]'::jsonb),
('2', 'barb', '[
{
"employees": [{"id":3}],
"coworkers": []
},
{
"employees": [{"id":3},{"id":4}],
"coworkers": [{"id":5, "id":3}]
}
]'::jsonb),
('3', 'ann', '[]'::jsonb),
('4', 'jeff', '[]'::jsonb),
('5', 'rachel', '[]'::jsonb),
('6', 'ryan', '[]'::jsonb);
请参阅:http://sqlfiddle.com/#!17/7c7ef/12/0
我试图简单地为每个同事和员工添加名称,以便bob看起来像:
[
{
"employees": [{"id":2, "name":"barb"},{"id":3, "name":"ann"},{"id":4, "jeff"}],
"coworkers": [{"id":5, "name":"rachel"},{"id":6, "name":"ryan"}]
},
{
"employees": [{"id":3, "name":"ann"},{"id":4, "name":"jeff"}],
"coworkers": [{"id":5, "name":"rachel"}]
}
]
到目前为止,我有:
SELECT c.person person
FROM tester
LEFT JOIN LATERAL(
SELECT jsonb_agg(
jsonb_build_object(
'employees', c.wrk->'employees',
'coworkers', c.wrk->'coworkers'
)
) AS person
FROM jsonb_array_elements(tester.d) AS c(wrk)
) c ON true
除了名字之外,它会返回所有内容:
[{"coworkers": [{"id": 5}, {"id": 6}], "employees": [{"id": 2}, {"id": 3}, {"id": 4}]}, {"coworkers": [{"id": 5}], "employees": [{"id": 3}, {"id": 4}]}]
[{"coworkers": [], "employees": [{"id": 3}]}, {"coworkers": [{"id": 3}], "employees": [{"id": 3}, {"id": 4}]}]
(null)
(null)
(null)
(null)
请注意对象列表:它们是单独的对象,而不仅仅是一个大对象。
"(null)" s / b一个空白数组" []"。
答案 0 :(得分:0)
使用两个横向连接,我们可以为同事和员工创建数组,我们在横向查询中连接到tester表以获取名称,然后构造jsonb对象&聚合以获得转换后的数组。
结果查询很多,但并不过分复杂。
SELECT
"name"
, CASE
WHEN d = '[]'::jsonb THEN NULL
ELSE coworker.people || employee.people
END relationships
FROM tester
, LATERAL (
SELECT
jsonb_build_object(
'coworkers'
, JSON_AGG(json_build_object('id', id, 'name', "name"))
) people
FROM (SELECT DISTINCT
(jsonb_array_elements(el->'coworkers')->>'id')::int id
FROM jsonb_array_elements(d) el) coworker
NATURAL JOIN tester
) coworker
, LATERAL (
SELECT
jsonb_build_object(
'employees'
, JSON_AGG(json_build_object('id', id, 'name', "name"))) people
FROM (SELECT DISTINCT
(jsonb_array_elements(el->'employees')->>'id')::int id
FROM jsonb_array_elements(d) el) employee
NATURAL JOIN tester
) employee
对象列表的替代解决方案:
WITH people_separated AS (
SELECT
"name"
, coworkers
, employees
FROM tester
, LATERAL (
SELECT
k->'coworkers' coworkers
, k->'employees' employees
FROM jsonb_array_elements(d) k
) split
)
, people_relationships AS (
SELECT
name
, JSON_AGG(
CASE WHEN e.people IS NULL THEN '{}'::jsonb ELSE jsonb_build_object('employees', e.people) END
||
CASE WHEN c.people IS NULL THEN '{}'::jsonb ELSE jsonb_build_object('coworkers', c.people) END
) relationships
FROM people_separated
, LATERAL (
SELECT JSON_AGG(
c || jsonb_build_object(
'name'
, (SELECT name FROM tester WHERE id = (c->>'id')::int)
)
) people
FROM jsonb_array_elements(coworkers) c) c
, LATERAL (
SELECT JSON_AGG(
e || jsonb_build_object(
'name'
, (SELECT name FROM tester WHERE id = (e->>'id')::int)
)
) people
FROM jsonb_array_elements(employees) e) e
GROUP BY 1
)
SELECT name, relationships FROM tester LEFT JOIN people_relationships USING (name)
答案 1 :(得分:0)
假设tester.id
是PK,以简化聚合:
SELECT t.id, t.name, COALESCE(t1.d, t.d)
FROM tester t
LEFT JOIN LATERAL (
SELECT jsonb_agg(jsonb_build_object('coworkers', COALESCE(c.coworkers, jsonb '[]'))
|| jsonb_build_object('employees', COALESCE(e.employees, jsonb '[]'))) AS d
FROM jsonb_array_elements(t.d) AS d1(p)
CROSS JOIN LATERAL (
SELECT jsonb_agg(p.id || jsonb_build_object('name', n.name)) AS coworkers
FROM jsonb_array_elements(d1.p ->'coworkers') AS p(id)
LEFT JOIN tester n ON n.id = (p.id->>'id')::int
) c
CROSS JOIN LATERAL (
SELECT jsonb_agg(p.id || jsonb_build_object('name', n.name)) AS employees
FROM jsonb_array_elements(d1.p ->'employees') AS p(id)
LEFT JOIN tester n ON n.id = (p.id->>'id')::int
) e
GROUP BY t.id
) t1 ON t.d <> '[]';
解释与我引用的旧答案很相似:
一个特殊的难点是保留空JSON数组'[]'
,其中聚合将返回NULL值,我使用COALESCE()
的战略用途解决了这个问题。
另一个是你希望将嵌套数组分开。解决了将未版本化阵列聚合回JSON数组的问题,在同事和员工的两个独立LATERAL
联接中。
请注意您的数据中的陷阱:"coworkers": [{"id":5, "id":3}]
SELECT jsonb '[{"id":5, "id":3}]'
会产生'[{"id": 3}]'
。也许你打算写'[{"id":5}, {"id":3}]'
?