Postgres - 多级父子关系数据
Postgres - multilevel parent child relation data
我们有如下的父子关系。
生成表格的脚本如下。
create table dependency ( packageid int, dependant_packageid int);
insert into dependency values (2,1);
insert into dependency values (3,1);
insert into dependency values (4,1);
insert into dependency values (5,2);
insert into dependency values (6,3);
insert into dependency values (7,4);
insert into dependency values (7,5);
insert into dependency values (8,5);
insert into dependency values (8,3);
insert into dependency values (4,5);
insert into dependency values (6,4);
insert into dependency values (5,3);
我们想根据下面提到的问题获取数据。
- 对于给定的包获取可能的依赖层次结构
Ex:
packageid : 6
Result should be: [(3,1),(4,1),(4,5,2,1),(4,5,3,1)]
packageid : 7
Result should be: [(4,1),(4,5,2,1),(4,5,3,1)]
packageid : 8
Result should be: [(5,2,1),(5,3,1),(3,1)]
- 对于给定的包获取父包列表
Ex:
1 - 2,3,4
2 - 5
3 - 6,8,5
4 - 7,6
5 - 7,8,4
- 如果我们需要维护这种父子关系(多对多),理想的模式结构应该是什么(牢记性能)?
感谢任何帮助....编码愉快....:)
这是一个递归查询的快速截图,该查询为每个 ID 吐出您独特的完整依赖项列表:
WITH RECURSIVE rcte AS (
--Recursive Seed
SELECT packageid as initialid,
packageid,
dependant_packageid,
CAST(packageid || ',' || dependant_packageid as varchar(30)) as path,
1 as depth
FROM dependency
UNION ALL
--Recursive Term
SELECT initialid,
dependency.packageid,
dependency.dependant_packageid,
CAST(rcte.path || ',' || dependency.dependant_packageid as varchar(30)),
rcte.depth + 1
FROM rcte
INNER JOIN dependency ON rcte.dependant_packageid = dependency.packageid
)
SELECT r1.initialid as packageid, path as dependant_packages
FROM rcte r1
LEFT OUTER JOIN rcte r2
ON r2.path LIKE r1.path || '%' AND r1.depth < r2.depth
WHERE r2.initialid IS NULL
ORDER BY r1.path;
递归 CTE 有两个部分。 recursive seed
是 运行 一次。它从 table(s) 中收集记录,这些记录最初将被送入第二部分,recursive term
将迭代直到其 JOIN 失败。在那个递归术语中,我们将 CTE rcte
连接回 table,并将 CTE 的 dependant_packageid
连接到 table 的 packageid
。
最后,SELECT
语句引用了 CTE 和自连接的结果,以从所有这些迭代中找到最长的不同路径。
使用相同的递归逻辑,您可以获得剩余的记录集。
我建议为您的 table 添加一个主键:
create table dependency (serial id, packageid int, dependant_packageid int);
然后,要获取层次结构,您可以使用如下查询:
WITH RECURSIVE rcte AS (
SELECT id,
packageid AS initial_packageid,
dependant_packageid,
ARRAY[dependant_packageid::text]::text[] as path,
1 as depth
FROM dependency
UNION ALL
SELECT rcte.id,
rcte.initial_packageid,
dependency.dependant_packageid,
rcte.path || dependency.dependant_packageid::text,
rcte.depth + 1
FROM rcte
JOIN dependency ON rcte.dependant_packageid = dependency.packageid ),
cte_hierarchy AS (
SELECT initial_packageid AS packageid,
(ARRAY_AGG( '(' || ARRAY_TO_STRING(path, ',') || ')' ORDER BY depth DESC))[1] AS hierarchy
FROM rcte
GROUP BY id, initial_packageid )
SELECT packageid, STRING_AGG(hierarchy, ',')
FROM cte_hierarchy
GROUP BY packageid
ORDER BY packageid
要获取父包,只需使用:
SELECT dependant_packageid AS packageid, ARRAY_AGG(DISTINCT packageid)
FROM dependency
GROUP BY dependant_packageid
ORDER BY dependant_packageid
我们有如下的父子关系。
生成表格的脚本如下。
create table dependency ( packageid int, dependant_packageid int);
insert into dependency values (2,1);
insert into dependency values (3,1);
insert into dependency values (4,1);
insert into dependency values (5,2);
insert into dependency values (6,3);
insert into dependency values (7,4);
insert into dependency values (7,5);
insert into dependency values (8,5);
insert into dependency values (8,3);
insert into dependency values (4,5);
insert into dependency values (6,4);
insert into dependency values (5,3);
我们想根据下面提到的问题获取数据。
- 对于给定的包获取可能的依赖层次结构
Ex:
packageid : 6
Result should be: [(3,1),(4,1),(4,5,2,1),(4,5,3,1)]
packageid : 7
Result should be: [(4,1),(4,5,2,1),(4,5,3,1)]
packageid : 8
Result should be: [(5,2,1),(5,3,1),(3,1)]
- 对于给定的包获取父包列表
Ex:
1 - 2,3,4
2 - 5
3 - 6,8,5
4 - 7,6
5 - 7,8,4
- 如果我们需要维护这种父子关系(多对多),理想的模式结构应该是什么(牢记性能)?
感谢任何帮助....编码愉快....:)
这是一个递归查询的快速截图,该查询为每个 ID 吐出您独特的完整依赖项列表:
WITH RECURSIVE rcte AS (
--Recursive Seed
SELECT packageid as initialid,
packageid,
dependant_packageid,
CAST(packageid || ',' || dependant_packageid as varchar(30)) as path,
1 as depth
FROM dependency
UNION ALL
--Recursive Term
SELECT initialid,
dependency.packageid,
dependency.dependant_packageid,
CAST(rcte.path || ',' || dependency.dependant_packageid as varchar(30)),
rcte.depth + 1
FROM rcte
INNER JOIN dependency ON rcte.dependant_packageid = dependency.packageid
)
SELECT r1.initialid as packageid, path as dependant_packages
FROM rcte r1
LEFT OUTER JOIN rcte r2
ON r2.path LIKE r1.path || '%' AND r1.depth < r2.depth
WHERE r2.initialid IS NULL
ORDER BY r1.path;
递归 CTE 有两个部分。 recursive seed
是 运行 一次。它从 table(s) 中收集记录,这些记录最初将被送入第二部分,recursive term
将迭代直到其 JOIN 失败。在那个递归术语中,我们将 CTE rcte
连接回 table,并将 CTE 的 dependant_packageid
连接到 table 的 packageid
。
最后,SELECT
语句引用了 CTE 和自连接的结果,以从所有这些迭代中找到最长的不同路径。
使用相同的递归逻辑,您可以获得剩余的记录集。
我建议为您的 table 添加一个主键:
create table dependency (serial id, packageid int, dependant_packageid int);
然后,要获取层次结构,您可以使用如下查询:
WITH RECURSIVE rcte AS (
SELECT id,
packageid AS initial_packageid,
dependant_packageid,
ARRAY[dependant_packageid::text]::text[] as path,
1 as depth
FROM dependency
UNION ALL
SELECT rcte.id,
rcte.initial_packageid,
dependency.dependant_packageid,
rcte.path || dependency.dependant_packageid::text,
rcte.depth + 1
FROM rcte
JOIN dependency ON rcte.dependant_packageid = dependency.packageid ),
cte_hierarchy AS (
SELECT initial_packageid AS packageid,
(ARRAY_AGG( '(' || ARRAY_TO_STRING(path, ',') || ')' ORDER BY depth DESC))[1] AS hierarchy
FROM rcte
GROUP BY id, initial_packageid )
SELECT packageid, STRING_AGG(hierarchy, ',')
FROM cte_hierarchy
GROUP BY packageid
ORDER BY packageid
要获取父包,只需使用:
SELECT dependant_packageid AS packageid, ARRAY_AGG(DISTINCT packageid)
FROM dependency
GROUP BY dependant_packageid
ORDER BY dependant_packageid