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);

我们想根据下面提到的问题获取数据。

  1. 对于给定的包获取可能的依赖层次结构

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)]
  1. 对于给定的包获取父包列表

Ex:
1 - 2,3,4
2 - 5
3 - 6,8,5
4 - 7,6
5 - 7,8,4
  1. 如果我们需要维护这种父子关系(多对多),理想的模式结构应该是什么(牢记性能)?

感谢任何帮助....编码愉快....:)

这是一个递归查询的快速截图,该查询为每个 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