PostgreSQL 9.1'i, düğümlere bağlantılar içeren kenarlardan (veya öğelerden) oluşan hiyerarşik ağaç yapılandırılmış verileri sorgulamak için kullanıyorum. Veriler aslında akış ağları içindir, ancak sorunu basit veri türleri için soyutladım. Örnek tree
tablosunu düşünün. Her kenar, ağdaki bazı yararlı metrikleri belirlemek için kullanılan uzunluk ve alan özelliklerine sahiptir.Özyinelemeli sorgular kullanılarak bir hiyerarşik ağaç yapısı yapısını geriye doğru nasıl geçilir?
CREATE TEMP TABLE tree (
edge text PRIMARY KEY,
from_node integer UNIQUE NOT NULL, -- can also act as PK
to_node integer REFERENCES tree (from_node),
mode character varying(5), -- redundant, but illustrative
length numeric NOT NULL,
area numeric NOT NULL,
fwd_path text[], -- optional ordered sequence, useful for debugging
fwd_search_depth integer,
fwd_length numeric,
rev_path text[], -- optional unordered set, useful for debugging
rev_search_depth integer,
rev_length numeric,
rev_area numeric
);
CREATE INDEX ON tree (to_node);
INSERT INTO tree(edge, from_node, to_node, mode, length, area) VALUES
('A', 1, 4, 'start', 1.1, 0.9),
('B', 2, 4, 'start', 1.2, 1.3),
('C', 3, 5, 'start', 1.8, 2.4),
('D', 4, 5, NULL, 1.2, 1.3),
('E', 5, NULL, 'end', 1.1, 0.9);
Aşağıda, A-E tarafından temsil edilen kenarların 1-5 no'lu düğümlerle bağlandığı gösterilebilir. NULL to_node
(Ø), son düğümü temsil eder. from_node
her zaman benzersizdir, bu yüzden PK olarak davranabilir. Bu ağ bir drainage basin gibi akar, bu üstten başlayarak kolu kenarları, A, B, C ve sona eren çıkış kenarı E
documentation for WITH
güzel bir örnek sağlamaktır olan alt olacaktır özyinelemeli sorgularda arama grafiklerini kullanma. Yani, "ileri" bilgi almak için, sorgu sonunda başlar ve geriye doğru çalışır:
WITH RECURSIVE search_graph AS (
-- Begin at ending nodes
SELECT E.from_node, 1 AS search_depth, E.length
, ARRAY[E.edge] AS path -- optional
FROM tree E WHERE E.to_node IS NULL
UNION ALL
-- Accumulate each edge, working backwards (upstream)
SELECT o.from_node, sg.search_depth + 1, sg.length + o.length
, o.edge|| sg.path -- optional
FROM tree o, search_graph sg
WHERE o.to_node = sg.from_node
)
UPDATE tree SET
fwd_path = sg.path,
fwd_search_depth = sg.search_depth,
fwd_length = sg.length
FROM search_graph AS sg WHERE sg.from_node = tree.from_node;
SELECT edge, from_node, to_node, fwd_path, fwd_search_depth, fwd_length
FROM tree ORDER BY edge;
edge | from_node | to_node | fwd_path | fwd_search_depth | fwd_length
------+-----------+---------+----------+------------------+------------
A | 1 | 4 | {A,D,E} | 3 | 3.4
B | 2 | 4 | {B,D,E} | 3 | 3.5
C | 3 | 5 | {C,E} | 2 | 2.9
D | 4 | 5 | {D,E} | 2 | 2.3
E | 5 | | {E} | 1 | 1.1
yukarıdaki mantıklı ve büyük ağlar için iyi ölçekler. Örneğin, kenardan B
kenarının 3 kenarı olduğunu ve ön yolun uçtan uca 3.5 toplam uzunluğu olan {B,D,E}
olduğunu görebiliyorum. Ancak, bir ters sorgu oluşturmak için iyi bir yol bulamıyorum. Yani, her kenardan, birikmiş "yukarı yönlü" kenarlar, uzunluklar ve alanlar. WITH RECURSIVE
kullanarak, en iyisi bu: Her alt kenar, 1 veya daha çok üst kenarları, ancak aggregates are not allowed with recursive queries bağlandığı için, yinelemeli terimi ikinci terime agrega inşa etmek isteyen
WITH RECURSIVE search_graph AS (
-- Begin at starting nodes
SELECT S.from_node, S.to_node, 1 AS search_depth, S.length, S.area
, ARRAY[S.edge] AS path -- optional
FROM tree S WHERE from_node IN (
-- Starting nodes have a from_node without any to_node
SELECT from_node FROM tree EXCEPT SELECT to_node FROM tree)
UNION ALL
-- Accumulate edges, working forwards
SELECT c.from_node, c.to_node, sg.search_depth + 1, sg.length + c.length, sg.area + c.area
, c.edge || sg.path -- optional
FROM tree c, search_graph sg
WHERE c.from_node = sg.to_node
)
UPDATE tree SET
rev_path = sg.path,
rev_search_depth = sg.search_depth,
rev_length = sg.length,
rev_area = sg.area
FROM search_graph AS sg WHERE sg.from_node = tree.from_node;
SELECT edge, from_node, to_node, rev_path, rev_search_depth, rev_length, rev_area
FROM tree ORDER BY edge;
edge | from_node | to_node | rev_path | rev_search_depth | rev_length | rev_area
------+-----------+---------+----------+------------------+------------+----------
A | 1 | 4 | {A} | 1 | 1.1 | 0.9
B | 2 | 4 | {B} | 1 | 1.2 | 1.3
C | 3 | 5 | {C} | 1 | 1.8 | 2.4
D | 4 | 5 | {D,A} | 2 | 2.3 | 2.2
E | 5 | | {E,C} | 2 | 2.9 | 3.3
. Ayrıca,
with recursive
sonucu
edge
için çoklu birleştirme koşullarına sahip olduğundan, katılımın özensiz olduğunu biliyorum.
ters için beklenen sonuç/geri sorgusu:
Nasıl bu güncelleştirme sorgusu yazabilir
edge | from_node | to_node | rev_path | rev_search_depth | rev_length | rev_area
------+-----------+---------+-------------+------------------+------------+----------
A | 1 | 4 | {A} | 1 | 1.1 | 0.9
B | 2 | 4 | {B} | 1 | 1.2 | 1.3
C | 3 | 5 | {C} | 1 | 1.8 | 2.4
D | 4 | 5 | {A,B,D} | 3 | 3.5 | 3.5
E | 5 | | {A,B,C,D,E} | 5 | 6.4 | 6.8
?
Nihayetinde, doğru uzunluk ve alan toplamları biriktirme konusunda daha fazla endişe duyduğumu ve yol özniteliklerinin hata ayıklama için olduğunu unutmayın. Gerçek dünyadaki durumumda, ileriye giden yollar birkaç yüz kadardır ve büyük ve karmaşık havzalar için on binlerce insanda ters yollar bekliyorum.
Veri modeli bir tabloya kenarları ve köşeleri birleştirmek için çalışır. Bunları bölmek, daha temiz bir model, IMO verecektir. – wildplasser
@wildplasser Eğer yardımcı olduğunu düşünüyorsanız, bunu bölmek kolaydır. Ayrıca, 'from_node' benzersiz olduğundan dolayı 'edge'i göz ardı edebilirsiniz. From_node, –
mantıksal olarak birincil anahtardır. Ve to_node yabancı anahtar başvuru ağacı olmalıdır (from_node) kenar işlevsel olarak from_node bağlıdır. Normalde, root için to_node, NULL (root'un ebeveyni yoktur) anlamına gelir, yani 'E' segmenti kaybolur veya ekstra bir düğüme ihtiyacınız vardır {from_node = 6, to_node = NULL} 'mode' alanı daha fazla veya Daha az yedek: sadece bu 'düğüm' noktasına işaret eden kayıtların var olduğunu gösterir. – wildplasser