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

时间:2015-11-27 12:56:01

标签: postgresql recursion tree depth

我有一个表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)

我现在想写一个类似打印所有后代的查询,以及深度。这是目前为止的查询(与上面相同,在UNION联接中交换了idparent_id):

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()并没有做我想做的事。我该怎么做?

我已经考虑过在查询本身的递归部分中使用一个计数器,它在每次运行时都会递增,但是我不确定是否有实现这一目的的方法。< / p>

1 个答案:

答案 0 :(得分:9)

使用其他列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)