如何在使用RECURSIVE选择子孙的Postgres查询中打印深度级别?

问题描述 投票:8回答:1

我有一个表 persons 的列,其中包含一个 parent_id,它指的是同一张表中的另一行。假设这就是逻辑层次结构。

          P1
  P2      P3      P4
P5  P6  P7  P8  P9  P10

我写了一个查询,打印出一个节点的所有父节点 以及该节点上面的高度,看起来效果很好。

WITH
RECURSIVE ancestors AS (
  SELECT id, parent_id
    FROM persons
    WHERE id = 8
  UNION
    SELECT p.id, p.parent_id
      FROM persons p
      INNER JOIN ancestors
        ON
          p.id = ancestors.parent_id
  )
SELECT persons.id, persons.name,
      ROW_NUMBER() over () as height
  FROM ancestors
  INNER JOIN persons
  ON
    ancestors.id = persons.id
  WHERE
    persons.id <> 8

结果:

  id   |    name     | height 
-------+-------------+---------
    3  | P3          |      1
    1  | P1          |      2
(2 rows)

现在我想写一个查询,同样打印所有子代,以及深度。下面是目前的查询结果(与上面的 idparent_id 在UNION join中交换)。)

WITH
RECURSIVE descendants AS (
  SELECT id, parent_id
    FROM persons
    WHERE id = 1
  UNION
    SELECT p.id, p.parent_id
      FROM persons p
      INNER JOIN descendants
        ON
          p.parent_id = descendants.id
  )
SELECT persons.id, persons.name,
      ROW_NUMBER() over () as depth
  FROM descendants
  INNER JOIN persons
  ON
    descendants.id = persons.id
  WHERE
    persons.id <> 1

这就得到了以下结果。

  id   |    name     | depth
-------+-------------+---------
    2  | P2          |      1
    3  | P3          |      2
    4  | P4          |      3
    5  | P5          |      4
    6  | P6          |      5
    7  | P7          |      6
    8  | P8          |      7
    9  | P9          |      8
    10 | P10         |      9
(9 rows)

很明显,深度是完全错误的 ROW_NUMBER() 并没有达到我想要的效果。我该如何解决这个问题?

我想过在查询本身的递归部分使用一个计数器,每次运行时都会递增,但我不确定是否有办法实现。

postgresql recursion tree recursive-query depth
1个回答
15
投票

使用附加列 depth:

WITH RECURSIVE descendants AS (
    SELECT id, parent_id, 0 depth
    FROM persons
    WHERE id = 1
UNION
    SELECT p.id, p.parent_id, d.depth+ 1
    FROM persons p
    INNER JOIN descendants d
    ON p.parent_id = d.id
)
SELECT p.id, p.name, depth
FROM descendants d
INNER JOIN persons p
ON d.id = p.id
WHERE p.id <> 1;

 id | name | depth 
----+------+-------
  2 | P2   |     1
  3 | P3   |     1
  4 | P4   |     1
  5 | P5   |     2
  6 | P6   |     2
  7 | P7   |     2
  8 | P8   |     2
  9 | P9   |     2
 10 | P10  |     2
(9 rows)
© www.soinside.com 2019 - 2024. All rights reserved.