Özyinelemeli bir sorgunun sonuçlarını, ağaç benzeri bir şekilde nasıl sıralayabilirim?


12

Diyelim ki böyle bir nodestablonuz var :

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

Kök düğümleri ve kök düğümlerden veya diğer alt düğümlerden sarkan birkaç alt düğüm içeren standart bir düğüm benzeri ağaç yapısını temsil eder.

Birkaç örnek değer ekleyelim:

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

Şimdi ilk 10 kök düğümü ve tüm çocuklarını 4 derinliğe kadar almak istiyorum:

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;

Bu harika çalışıyor ve bana aşağıdaki sonucu veriyor:

 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

Fark etmiş olabileceğiniz gibi, hiçbir ORDER BYmadde yoktur , bu nedenle sipariş tanımlanmamıştır. Burada gördüğünüz sıra kök düğümlerden daha derin düğümlere kadar.

Aşağıdaki örnek resimde görebileceğiniz gibi, sonuçları genişletilmiş ağaç görünümünde göründüğü gibi nasıl sipariş edebilirim?

Düğümlerin genişletilmiş ağaç görünümü

Temel olarak alt düğümlerin karşılık gelen üst düğümden hemen sonra yerleştirilmesini istiyorum. İki veya daha fazla alt düğüm aynı ana düğüme sahipse, bunların zaman damgalarına göre sıralanmasını istiyorum. Yukarıdaki örneğe dayanarak, elde etmeye çalıştığım istenen çıktı sırası:

 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

Birisi bana depthsütunun nereden geldiğini açıklayabilir mi? İlk tablo yapısında görmüyorum.
sorin

@sorin, bunun gerçekten eski bir gönderi olduğunu biliyorum, ancak Google'da bununla karşılaştım ve sorunuzu cevaplayacağımı düşündüm. Derinlik, ilk sorgudaki '1' değişmezinin diğer adından gelir.
Sam

Yanıtlar:


11

Kökten yaprağa giden yolu temsil eden bir dizi, istenen sıralama düzenine ulaşmalıdır:

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;

İki veya daha fazla alt düğüm aynı ana düğüme sahipse, bunların zaman damgalarına göre sıralanmasını istiyorum.

Yolu birer birer köke doğru kaydırın ve ek olarak bu sütuna göre sıralayı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.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;

Teşekkür ederim, harika çalışıyor! Ancak, "İki veya daha fazla alt düğüm aynı üst düğüme sahipse, bunların zaman damgalarına göre sıralanmasını istiyorum" bölümüne ne dersiniz? Bu, bu yaklaşımla yapılabilir mi? Daha yüksek bir düğüm kimliğinin daha sonraki bir zamana karşılık gelmesi her zaman böyle olmayabilir.
JohnCand

@JohnCand: Yolu birer birer köke doğru kaydırabilirsiniz (kök düğümü ilk konumda tekrarlayın!) Ve bu sütuna göre sipariş verin ...
Erwin Brandstetter
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.