我在PostgreSQL中有一个包含以下内容的表:
id name arrayofparents
1 First
2 Second {1}
3 Second_Sec {1,2}
4 Third {1,2,3}
5 Second_A {1}
6 Other
7 Trash {6}
arrayofparents
的类型为integer[]
,它包含具有正确顺序的该行的父记录列表。
id=4
父母是:First
然后是Second
然后是Second_sec
如何编写一个查询,对于任何给定的id,它将生成一个字符串的父母姓名?
例如:
id=3
:First->Second
。
id=4
:First->Second->Second_sec
。
id=7
:Other
。
修改
如果可能,我希望始终显示请求的ID name
。
id=3
:First->Second->Second_sec
。
id=4
:First->Second->Second_sec->Third
。
id=7
:Other->Trash
。
id=6
:Other
。
答案 0 :(得分:2)
你可以结合使用generate_subscripts和array之类的多种操作来获得结果:
with mtab as (
SELECT id, name, array_append(arrayofparents,id) as arrayofparents,
generate_subscripts(array_append(arrayofparents, id), 1) AS p_id FROM tab where id=2
)
select distinct array_to_string(
array(
select tab.name from tab join mtab t on tab.id=t.arrayofparents[t.p_id]
), '->'
) ;
或使用外部联接与任何:
结合使用SELECT coalesce(string_agg(p.name, '->') || '->' || t.name, t.name) AS parentnames
FROM tab AS t
LEFT JOIN tab AS p ON p.id = ANY(t.arrayofparents)
where t.id =7
GROUP BY t.id, t.name
答案 1 :(得分:1)
这些查询中的每一个都适用于单个ID以及整个表 而且您也可以只返回路径/完整路径或所有其他列。
SELECT t.*, concat_ws('->', t1.path, t.name) AS full_path
FROM tbl t
LEFT JOIN LATERAL (
SELECT string_agg(t1.name, '->' ORDER BY i) AS path
FROM generate_subscripts(t.arrayofparents, 1) i
JOIN tbl t1 ON t1.id = t.arrayofparents[i]
) t1 ON true
WHERE t.id = 4; -- optional
或者,您可以将ORDER BY
移动到子查询 - 可能会更快一些:
SELECT concat_ws('->', t1.path, t.name) AS full_path
FROM tbl t, LATERAL (
SELECT string_agg(t1.name, '->') AS path
FROM (
SELECT t1.name
FROM generate_subscripts(t.arrayofparents, 1) i
JOIN tbl t1 ON t1.id = t.arrayofparents[i]
ORDER BY i
) t1
) t1
WHERE t.id = 4; -- optional
由于聚合发生在LATERAL
子查询中,我们在外部查询中不需要GROUP BY
步。
我们也不需要LEFT JOIN LATERAL ... ON true
来保留arrayofparents
为空或空的所有行,因为LATERAL
子查询总是返回一行由于集合功能。
LATERAL
需要Postgres 9.3 。
使用concat_ws()
忽略串联中可能的NULL值。
WITH OTDINALITY
使Postgres中的内容更简单,更快 9.4 :
SELECT t.*, concat_ws('->', t1.path, t.name) AS full_path
FROM tbl t, LATERAL (
SELECT string_agg(t1.name, '->' ORDER BY ord) AS path
FROM unnest(t.arrayofparents) WITH ORDINALITY a(id,ord)
JOIN tbl t1 USING (id)
) t1
WHERE t.id = 4;
详细说明:
对于第9页的UNION ALL
变体
SELECT t1.full_path
FROM tbl t, LATERAL (
SELECT string_agg(name, '->') AS full_path
FROM (
(
SELECT name
FROM generate_subscripts(t.arrayofparents, 1) i
JOIN tbl ON id = t.arrayofparents[i]
ORDER BY i
)
UNION ALL SELECT t.name
) t1
) t1
WHERE t.id = 4;
答案 2 :(得分:0)
如果你只想要直接父母(不是祖父母),那么这样的事情应该有效:
SELECT c.id, c.name, string_agg(p.name, '->') AS parentnames
FROM yourtable AS c
LEFT JOIN yourtable AS p ON p.id = ANY c.parents
GROUP BY c.id, c.name