Làm cách nào để sắp xếp kết quả của truy vấn đệ quy theo kiểu mở rộng giống như cây?


12

Giả sử bạn có một nodesbảng như thế này:

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

Nó đại diện cho một cấu trúc cây giống như nút tiêu chuẩn với các nút gốc ở trên cùng và một số nút con treo lủng lẳng từ các nút gốc hoặc các nút con khác.

Hãy để chúng tôi chèn một vài giá trị mẫu:

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

Bây giờ tôi muốn lấy 10 nút gốc đầu tiên và tất cả các con của chúng lên tới độ sâu 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;

Điều này hoạt động rất tốt và cho tôi kết quả như sau:

 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

Như bạn có thể nhận thấy, không có ORDER BYmệnh đề nào , vì vậy thứ tự không được xác định. Thứ tự bạn thấy ở đây là từ các nút gốc đến các nút sâu hơn.

Làm thế nào tôi có thể sắp xếp các kết quả như chúng sẽ xuất hiện trong chế độ xem dạng cây mở rộng, như bạn có thể thấy từ hình ảnh ví dụ bên dưới?

Chế độ xem cây mở rộng của các nút

Về cơ bản tôi muốn các nút con được đặt ngay sau nút cha tương ứng của chúng. Nếu hai hoặc nhiều nút con có cùng nút cha, tôi muốn chúng được sắp xếp theo dấu thời gian của chúng. Dựa trên ví dụ trên, đây là thứ tự đầu ra mong muốn mà tôi đang cố gắng đạt được:

 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

Ai đó có thể giải thích cho tôi có depthcột đến từ đâu? Tôi không thấy nó trong cấu trúc bảng ban đầu.
sorin

@sorin, tôi biết đây là một bài viết cũ thực sự, nhưng tôi chỉ tình cờ thấy nó trên Google và nghĩ rằng tôi sẽ trả lời câu hỏi của bạn. Độ sâu xuất phát từ bí danh của chữ "1" trong truy vấn đầu tiên.
Sam

Câu trả lời:


11

Một mảng biểu thị đường dẫn từ gốc đến lá sẽ đạt được thứ tự sắp xếp mong muốn:

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;

Nếu hai hoặc nhiều nút con có cùng nút cha, tôi muốn chúng được sắp xếp theo dấu thời gian của chúng.

Thay đổi đường dẫn theo một hướng về phía gốc và sắp xếp theo cột đó:

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;

Cảm ơn bạn, nó hoạt động tuyệt vời! Tuy nhiên, còn phần "Nếu hai hoặc nhiều nút con có cùng nút cha, tôi muốn chúng được sắp xếp theo dấu thời gian của chúng" thì sao? Điều này có thể thực hiện được với phương pháp này? Nó có thể không phải luôn luôn là trường hợp ID nút cao hơn tương ứng với lần sau.
JohnCand

@JohnCand: Bạn có thể chuyển đường dẫn theo từng hướng về gốc (lặp lại nút gốc ở vị trí đầu tiên!) Và sắp xếp theo cột đó thêm ...
Erwin Brandstetter
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.