2つのテーブルがあります
ID Task
1 1
2 2
3 3
4 4
Col1 depend
2 3
2 4
3 1
4 2
ID
およびCol1
は、FK制約によって関連付けられています。すべての循環参照を見つけたい。ここでID
とCol1
は、2つのテーブルの行を結合するためのものです。例:
Task 1 can start anytime.
Task 2 can start only after completion of 3, 4 etc
1 –
2 – 3, 4, 1, 2 -- for 2 there is circular dependency
3 – 1
4 – 2, 3, 4 -- also circular dependency
ケース2:
Col1 depend
2 3
2 4
3 1
4 5
5 2
ID Task
1 1
2 2
3 3
4 4
5 5
1
2 – 3, 4, 1, 5, 2 -- circular reference
3 – 1
4 – 5, 2, 3, 4 -- circular reference
5 – 2, 3, 4, 5 -- circular reference
循環参照は、どの再帰レベルでも使用できます。そのような循環参照を見つける方法は?
再帰クエリを試しましたが、無限ループに入りました。これに対する再帰クエリを書く方法は?
http://www.postgresql.org/docs/8.4/static/queries-with.html の例をあなたのケースに適合させました:
WITH RECURSIVE search_graph(id, depends, depth, path, cycle) AS (
SELECT g.Col1, g.depends, 1,
ARRAY[g.Col1],
false
FROM deps g
UNION ALL
SELECT g.Col1, g.depends, sg.depth + 1,
path || g.Col1,
g.Col1 = ANY(path)
FROM deps g, search_graph sg
WHERE g.Col1 = sg.depends AND NOT cycle
)
SELECT distinct id FROM search_graph where cycle = true;
結果:
ID
4
2
最初の例では、
ID
4
2
5
二番目に
SQLフィドルは http://sqlfiddle.com/#!15/87a96/2 にあります。
マニュアルの例 にも基づいています:
WITH RECURSIVE graph AS (
SELECT col1 AS id
, ARRAY[depend, col1] AS path
, (depend = col1) AS cycle -- first step could be circular
FROM dep
UNION ALL
SELECT d.col1, d.depend || path, d.depend = ANY(path)
FROM graph g
JOIN dep d ON d.col1 = g.path[1]
WHERE NOT g.cycle
)
SELECT DISTINCT id
FROM graph
WHERE cycle;
結果
id
2
4
5
これは少し単純で高速です。
これはprepends
それぞれの新しいアイテムのパスなので、path[1]
追加の列の代わりに。私の知る限り、appending要素と同じくらい高速です。
配列の最初の要素へのアクセスは、追加の列とほぼ同じくらい安くなるはずです。これにより、行が広くなります。パフォーマンスが重要かどうかをテストして比較します。
WITH RECURSIVE graph AS (
SELECT col1 AS id, depend AS col1 -- simplify join condition
, ARRAY[col1, depend] AS path
, (col1 = depend) AS cycle -- simplify if short-circuit impossible
FROM dep
UNION ALL
SELECT d.col1, d.depend
, path || d.depend, d.depend = ANY(path)
FROM graph g
JOIN dep d USING (col1)
WHERE NOT g.cycle
)
SELECT DISTINCT id
FROM graph
WHERE cycle;