在 PostgreSQL 中递归聚合父项

kge*_*geo 1 postgresql recursion parent-child aggregate-functions

在子-父表中,我需要汇总每个子项的所有父项。我可以很容易地在 CTE 查询中为每个父母获取孩子,但无法弄清楚如何反转它(这里是 sqfiddle)。鉴于这种:

CREATE TABLE rel(
  child integer,
  parent integer
);

INSERT INTO rel(child, parent)
VALUES
(1,NULL),
(2,1),
(3,1),
(4,3),
(5,2),
(6,4),
(7,2),
(8,7),
(9,8);
Run Code Online (Sandbox Code Playgroud)

将返回父数组的查询(顺序不重要):

1, {NULL}
2, {1}
3, {1}
4, {3,1}
5, {2,1}
6, {4,3,1}
7, {2,1}
8, {7,2,1}
9, {8,7,2,1}
Run Code Online (Sandbox Code Playgroud)

Dar*_*rio 5

即使有一个公认的答案,我也想展示如何在纯 SQL 中以更简单的方式使用递归 CTE 解决问题:

WITH RECURSIVE t(child, parentlist) AS (
  SELECT child , ARRAY[]::INTEGER[] FROM rel WHERE parent IS NULL
  UNION
  SELECT rel.child, rel.parent || t.parentlist 
    FROM rel 
    JOIN t ON rel.parent = t.child
) SELECT * FROM t;


 child | parentlist 
-------+------------
     1 | {}
     2 | {1}
     3 | {1}
     4 | {3,1}
     5 | {2,1}
     7 | {2,1}
     6 | {4,3,1}
     8 | {7,2,1}
     9 | {8,7,2,1}
(9 rows)
Run Code Online (Sandbox Code Playgroud)

如果您坚持要{NULL}为父母列表为空的孩子提供单身人士,请说

SELECT child,
       CASE WHEN CARDINALITY(parentlist) = 0 
            THEN ARRAY[NULL]::INTEGER[]
            ELSE parentlist
       END
  FROM t;
Run Code Online (Sandbox Code Playgroud)

而不是SELECT * FROM t,但坦率地说,我不明白你为什么应该这样做。

最后一点:我不知道有什么有效的方法可以使用关系数据库来做到这一点,无论是使用纯 SQL 还是使用过程语言。关键是JOIN's 本质上是昂贵的,如果您有非常大的表,您的查询将花费大量时间。您可以使用索引来缓解问题,但解决此类问题的最佳方法是使用图形软件而不是 RDBMS。