如何以扩展的树状方式对递归查询的结果进行排序?


12

假设您有一个nodes这样的表:

CREATE TABLE nodes
(
    node serial PRIMARY KEY,
    parent integer NULL REFERENCES nodes(node),
    ts timestamp NOT NULL DEFAULT now()
);

它代表一个标准的类节点树结构,其中根节点在顶部,几个子节点与根节点或其他子节点悬挂在一起。

让我们插入几个示例值:

INSERT INTO nodes (parent)
VALUES (NULL), (NULL), (NULL), (NULL), (1), (1), (1), (1), (6), (1)
     , (6), (9), (6), (6), (3), (3), (3), (15);

现在,我要检索前10个根节点及其所有子节点,深度为4:

WITH RECURSIVE node_rec AS
(
    (SELECT 1 AS depth, * FROM nodes WHERE parent IS NULL LIMIT 10)

    UNION ALL

    SELECT depth + 1, n.*
    FROM nodes AS n JOIN node_rec ON (n.parent = node_rec.node)
    WHERE depth < 4
)
SELECT * FROM node_rec;

这很好用,并给我以下结果:

 depth | node | parent 
-------+------+--------
     1 |  1   |
     1 |  2   |
     1 |  3   |
     1 |  4   |
     2 |  5   |  1
     2 |  6   |  1
     2 |  7   |  1
     2 |  8   |  1
     2 | 10   |  1
     2 | 15   |  3
     2 | 16   |  3
     2 | 17   |  3
     3 |  9   |  6
     3 | 11   |  6
     3 | 13   |  6
     3 | 14   |  6
     3 | 18   | 15
     4 | 12   |  9

您可能已经注意到,由于没有ORDER BY子句,因此未定义顺序。您在此处看到的顺序是从根节点到更深的节点。

从下面的示例图片中可以看到,如何对结果显示在展开的树状视图中进行排序?

节点的展开树视图

我基本上希望将子节点放置在其相应的父节点之后。如果两个或多个子节点具有相同的父节点,我希望它们按照其时间戳进行排序。基于上面的示例,这是我尝试实现的所需输出顺序:

 depth | node | parent | ts
-------+------+--------+---------
     1 |  1   |        | 2014-01-01 00:00:00
     2 |  5   |     1  | 2014-01-01 00:10:00
     2 |  6   |     1  | 2014-01-01 00:20:00
     3 |  9   |     6  | 2014-01-01 00:25:00
     4 |  12  |     9  | 2014-01-01 00:27:00
     3 |  11  |     6  | 2014-01-01 00:26:00
     3 |  13  |     6  | 2014-01-01 00:30:00
     3 |  14  |     6  | 2014-01-01 00:36:00
     2 |  7   |     1  | 2014-01-01 00:21:00
     2 |  8   |     1  | 2014-01-01 00:22:00
     2 |  10  |     1  | 2014-01-01 00:23:00
     1 |  2   |        | 2014-01-01 00:08:00
     1 |  3   |        | 2014-01-01 00:09:00
     2 |  15  |     3  | 2014-01-01 10:00:00
     3 |  18  |     15 | 2014-01-01 11:05:00
     2 |  16  |     3  | 2014-01-01 11:00:00
     2 |  17  |     3  | 2014-01-01 12:00:00
     1 |  4   |        | 2014-01-01 00:10:00

有人可以解释一下该depth列的来源吗?我没有在初始表结构中看到它。
sorin

@sorin,我知道这是一篇很老的文章,但是我在Google上偶然发现它,以为我会回答你的问题。深度来自第一个查询中文字“ 1”的别名。
山姆

Answers:


11

表示从根到叶的路径的数组应达到所需的排序顺序:

WITH RECURSIVE node_rec AS (
   (SELECT 1 AS depth, ARRAY[node] AS path, *
    FROM   nodes
    WHERE  parent IS NULL
    LIMIT  10
   )    
    UNION ALL
    SELECT r.depth + 1, r.path || n.node, n.*
    FROM   node_rec r 
    JOIN   nodes    n ON n.parent = r.node
    WHERE  r.depth < 4
)
SELECT *
FROM   node_rec
ORDER  BY path;

如果两个或多个子节点具有相同的父节点,我希望它们按照其时间戳进行排序。

将路径向根方向移动一个,并按该列的顺序进行排序:

WITH RECURSIVE node_rec AS (
   (SELECT 1 AS depth, ARRAY[node] AS path, *
    FROM   nodes
    WHERE  parent IS NULL
    LIMIT  10
   )    
    UNION ALL
    SELECT r.depth + 1, r.path || n.parent, n.*
    FROM   node_rec r 
    JOIN   nodes    n ON n.parent = r.node
    WHERE  r.depth < 4
)
SELECT *
FROM   node_rec
ORDER  BY path, ts;

谢谢,效果很好!但是,“如果两个或多个子节点具有相同的父节点,我希望它们按其时间戳进行排序”部分怎么办?这种方法可行吗?较高的节点ID并不总是对应于较晚的时间。
JohnCand 2014年

@JohnCand:您可以将路径向根方向移动一个(在第一个位置重复根节点!)并按该列的顺序进行排序...
Erwin Brandstetter 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.