2017-02-12 10 views
1

階層リンク(parent_id、child_id)のグラフを表すテーブルがあります テーブルには親、子、両方のインデックスがあります。 グラフにはループが含まれている可能性があります。グラフを確認する必要があります(または、ループをすべて削除する必要があります)。postgresqlは再帰的クエリのインデックスを無視します

そして、私はノードのすべての親を再帰的にクエリする必要があります。このクエリはpostgresの場合

WITH RECURSIVE recursion(parent_id, child_id, node_id, path) AS (
    SELECT h.parent_id, 
     h.child_id, 
     h.child_id AS node_id, 
     ARRAY[h.parent_id, h.child_id] AS path 
     FROM hierarchy h 
    UNION ALL 
    SELECT h.parent_id, 
     h.child_id, 
     r.node_id, 
     ARRAY[h.parent_id] || r.path 
     FROM hierarchy h JOIN recursion r ON h.child_id = r.parent_id 
     WHERE NOT r.path @> ARRAY[h.parent_id] 
    ) 
SELECT parent_id, 
    child_id, 
    node_id, 
    path 
    FROM recursion 
    where node_id = 883 

非常に素晴らしい計画を使用する予定です:

"CTE Scan on recursion (cost=2703799682.88..4162807558.70 rows=324223972 width=56)" 
" Filter: (node_id = 883)" 
" CTE recursion" 
" -> Recursive Union (cost=0.00..2703799682.88 rows=64844794481 width=56)" 
"   -> Seq Scan on hierarchy h (cost=0.00..74728.61 rows=4210061 width=56)" 
"   -> Merge Join (cost=10058756.99..140682906.47 rows=6484058442 width=56)" 
"    Merge Cond: (h_1.child_id = r.parent_id)" 
"    Join Filter: (NOT (r.path @> ARRAY[h_1.parent_id]))" 
"    -> Index Scan using hierarchy_idx_child on hierarchy h_1 (cost=0.43..256998.25 rows=4210061 width=16)" 
"    -> Materialize (cost=10058756.56..10269259.61 rows=42100610 width=48)" 
"      -> Sort (cost=10058756.56..10164008.08 rows=42100610 width=48)" 
"       Sort Key: r.parent_id" 
"       -> WorkTable Scan on recursion r (cost=0.00..842012.20 rows=42100610 width=48)" 

Postgresはないように思え、私はこのクエリは(ビュー内に保存されることになっている)を使用し、このために 最初の再帰的サブクエリでnode_idの外部フィルタがchild_idに適用されることを理解してください。

私は非常に間違ったことをしていると思います。しかし、正確にどこですか?

+1

通常、上位ノード(親なし)またはリーフノード(子なし)、または関心のある特定のレコード番号の条件がUNIONの最初の部分にあります。 *チェインスターターとしてのすべての記録。 – wildplasser

+0

ユニオンの最初のクエリは、テーブルの**すべての**行を取得します。インデックスは役に立たない –

+0

私は、postgresが外側のフィルタを内部のサブクエリとマージできることを期待していました。私はあまりにも楽観的だと思われる。 – qMax

答えて

1

あなただけの労働組合の最初の部分にWHERE node_id = 883を移動する必要があるように見える:ここ

WITH RECURSIVE recursion(parent_id, child_id, node_id, path) AS (
    SELECT h.parent_id, 
     h.child_id, 
     h.child_id AS node_id, 
     ARRAY[h.parent_id, h.child_id] AS path 
     FROM hierarchy h 
     WHERE node_id = 883 
    UNION ALL 
    SELECT h.parent_id, 
     h.child_id, 
     r.node_id, 
     ARRAY[h.parent_id] || r.path 
     FROM hierarchy h JOIN recursion r ON h.child_id = r.parent_id 
     WHERE NOT r.path @> ARRAY[h.parent_id] 
    ) 
SELECT parent_id, 
    child_id, 
    node_id, 
    path 
    FROM recursion 
+0

このような場合、私はビューにクエリを保存することはできませんが、開始フィルタの入力パラメータを持つ関数にのみ行います。 – qMax

+1

関数はパラメータ化されたビューです:-)また、再帰的でない部分でクエリを高速化できる場所を使用することによってのみ –

+0

はい。この関数はおそらく私がここに必要なものでしょう。 – qMax

0

は、グラフトラバースタスクを解決するためにはるかに効果的な方法です。

CREATE OR REPLACE FUNCTION public.terr_ancestors(IN bigint) 
RETURNS TABLE(node_id bigint, depth integer, path bigint[]) AS 
$BODY$ 
WITH RECURSIVE recursion(node_id, depth, path) AS (
    SELECT $1 as node_id, 0, ARRAY[$1] AS path 
    UNION ALL 
    SELECT h.parent_id as node_id, r.depth + 1, h.parent_id || r.path 
    FROM hierarchy h JOIN recursion r ON h.child_id = r.node_id 
    WHERE h.parent_id != ANY(path) 
) 
SELECT * FROM recursion 
$BODY$ 

子孫の場合も同様です。

関連する問題