私は PostgreSQL 9.1 を使用して、ノードへの接続を持つエッジ (または要素) で構成される階層ツリー構造のデータをクエリしています。データは実際にはストリーム ネットワーク用ですが、問題を単純なデータ型に抽象化しました。例のtree
テーブルを考えてみましょう。各エッジには長さと面積の属性があり、ネットワークからいくつかの有用なメトリックを決定するために使用されます。
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);
これは、AE で表されるエッジがノード 1 ~ 5 に接続されている以下に示すことができます。NULL to_node
(Ø) は終了ノードを表します。はfrom_node
常に一意であるため、PK として機能できます。このネットワークが流域のように流れる場合、支流の開始エッジは A、B、C であり、流出エッジの終了は E です。
のドキュメントはWITH
、再帰クエリで検索グラフを使用する方法の良い例を提供します。したがって、「前方」情報を取得するために、クエリは最後から開始され、逆方向に動作します。
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
上記は理にかなっており、大規模なネットワークに適しています。たとえば、エッジB
は端から 3 エッジであり、前進パスは{B,D,E}
先端から端までの全長が 3.5 であることがわかります。
ただし、逆クエリを作成する良い方法がわかりません。つまり、各エッジから、蓄積された「上流」のエッジ、長さ、および面積はいくらかです。を使用してWITH RECURSIVE
、私が持っている最高のものは次のとおりです。
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
各ダウンストリーム エッジは 1 つまたは多数のアップストリーム エッジに接続するため、再帰クエリの 2 番目の項に集計を組み込みたいと考えていますが、再帰クエリでは集計が許可されていません。また、with recursive
結果には の結合条件が複数あるため、結合がずさんであることは承知してい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 | {A,B,D} | 3 | 3.5 | 3.5
E | 5 | | {A,B,C,D,E} | 5 | 6.4 | 6.8
この更新クエリを作成するにはどうすればよいですか?
最終的には、正確な長さと面積の合計を蓄積することに関心があり、パス属性はデバッグ用であることに注意してください。私の現実のケースでは、順方向のパスは最大で数百であり、大規模で複雑な集水域の逆方向のパスは数万になると予想しています。