递归 parent child 查询 - postgresql

Recursive parent child query - postgresql

我正在尝试准备一个递归查询,它将在单个 table.

中生成关于 parent-child 关系的数据

这是一些测试数据:

CREATE TABLE test
(
  id INTEGER,
  parent INTEGER
);


INSERT INTO test (id, parent) VALUES
  (2, 1),
  (3, 1),
  (7, 2),
  (8, 3),
  (9, 3),
  (10, 8),
  (11, 8);
    1       
   / \     
  2   3    
 /   / \
7   8   9  
   / \
 10   11
Excepted results:
--  id |    ancestry | parent | 
--   1 |         {1} |      1 |  
--   2 |       {1,2} |      1 |       
--   3 |       {1,3} |      1 |                 
--   7 |     {1,2,7} |      2 |                 
--   8 |     {1,3,8} |      3 |                 
--   9 |     {1,3,9} |      3 |                 
--  10 |  {1,3,8,10} |      8 |                 
--  11 |  {1,3,8,11} |      8 |       

#Query 1 : Return 所有 parents for children 11 - {1,3,8,11}。这里的问题是我不知道如何标记11的第一个parent是8.

WITH RECURSIVE c AS (
   SELECT 11 AS id
   UNION
   SELECT t.parent
   FROM test AS t
   JOIN c ON c.id = t.id
)
SELECT * FROM c;

提前致谢。

你可以携带一个计数器来查看在层次结构中的位置。

WITH RECURSIVE
c
AS
(
SELECT 11 AS id,
       0 AS n
UNION ALL
SELECT t.parent,
       c.n + 1
       FROM test AS t
            INNER JOIN c
                       ON c.id = t.id
)
SELECT *
       FROM c
       ORDER BY n;

8 在您的示例中将有 1,因此将其指定为第一个父级。
db<>fiddle


编辑:

在此基础上,您可以将锚点集扩展到所有节点。有点不幸的是没有 1 的条目。快速修复是 UNION ALL 也为它创建一条记录(但是你应该通过将记录 (1, NULL) 插入 test 来永久修复此问题.).让第三列携带层次结构开始的实际叶子和最后的GROUP BY。使用 array_agg() 获取祖先数组。 (过滤 NULL 节点,一旦你将 (1, NULL) 插入到 test 中,这些节点就会在那里。)要获得叶子的直接父级,你可以使用 max() (或 min()) 在计数器为 1 的 id 过滤上。

WITH RECURSIVE
c
AS
(
SELECT 1 AS leaf,
       1 AS parent,
       0 AS n
UNION ALL
SELECT id AS leaf,
       id AS parent,
       0 AS n
       FROM test
UNION ALL
SELECT leaf,
       t.parent,
       c.n + 1
       FROM test AS t
            INNER JOIN c
                       ON c.parent = t.id
)
SELECT leaf AS id,
       array_agg(parent ORDER BY n DESC) FILTER (WHERE parent IS NOT NULL) AS ancestry,
       max(parent) FILTER (WHERE n = 1) AS parent
       FROM c
       GROUP BY leaf
       ORDER BY leaf;

db<>fiddle