再帰クエリを使用したテーブル依存関係グラフの構築
-
01-07-2019 - |
質問
テーブル間の外部キーに基づいてテーブルの依存関係グラフを構築しようとしています。このグラフは、ルートとして任意のテーブル名で開始する必要があります。テーブル名を指定して、all_constraints ビューを使用してそれを参照するテーブルを検索し、次にそのテーブルを参照するテーブルを検索する、ということもできますが、これは非常に非効率的です。すべてのテーブルに対してこれを行う再帰クエリを作成しましたが、次のことを追加すると、
START WITH Table_Name=:tablename
ツリー全体は返されません。
解決
select parent, child, level from (
select parent_table.table_name parent, child_table.table_name child
from user_tables parent_table,
user_constraints parent_constraint,
user_constraints child_constraint,
user_tables child_table
where parent_table.table_name = parent_constraint.table_name
and parent_constraint.constraint_type IN( 'P', 'U' )
and child_constraint.r_constraint_name = parent_constraint.constraint_name
and child_constraint.constraint_type = 'R'
and child_table.table_name = child_constraint.table_name
and child_table.table_name != parent_table.table_name
)
start with parent = 'DEPT'
connect by prior child = parent
すべてが同じスキーマ内にあると仮定すると、機能するはずです(もちろんテーブル名を置き換えます)。スキーマ間の依存関係を処理する必要がある場合は、データ ディクショナリ テーブルの DBA_ バージョンと、OWNER 列と R_OWNER 列の条件を使用します。さらによく考えてみると、これは自己参照制約を考慮していません (つまり、MGR 列が EMPNO 列を参照するという EMP テーブルの制約)、自己参照制約を処理する必要がある場合は、そのケースを処理するようにコードを変更する必要があります。
テスト目的で、DEPT テーブル (孫の依存関係を含む) も参照するいくつかの新しいテーブルを SCOTT スキーマに追加しました。
SQL> create table dept_child2 (
2 deptno number references dept( deptno )
3 );
Table created.
SQL> create table dept_child3 (
2 dept_child3_no number primary key,
3 deptno number references dept( deptno )
4 );
Table created.
SQL> create table dept_grandchild (
2 dept_child3_no number references dept_child3( dept_child3_no )
3 );
Table created.
クエリが期待した出力を返したことを確認しました
SQL> ed
Wrote file afiedt.buf
1 select parent, child, level from (
2 select parent_table.table_name parent, child_table.table_name child
3 from user_tables parent_table,
4 user_constraints parent_constraint,
5 user_constraints child_constraint,
6 user_tables child_table
7 where parent_table.table_name = parent_constraint.table_name
8 and parent_constraint.constraint_type IN( 'P', 'U' )
9 and child_constraint.r_constraint_name = parent_constraint.constraint_name
10 and child_constraint.constraint_type = 'R'
11 and child_table.table_name = child_constraint.table_name
12 and child_table.table_name != parent_table.table_name
13 )
14 start with parent = 'DEPT'
15* connect by prior child = parent
SQL> /
PARENT CHILD LEVEL
------------------------------ ------------------------------ ----------
DEPT DEPT_CHILD3 1
DEPT_CHILD3 DEPT_GRANDCHILD 2
DEPT DEPT_CHILD2 1
DEPT EMP 1
他のヒント
これを行う最も簡単な方法は、すべての FK 情報を単純な 2 列 (親、子) テーブルにコピーし、次のアルゴリズムを使用することです。
while (rows left in that table)
list = rows where table name exists in child but not in parent
print list
remove list from rows
それだけです。基本的には、最初に何も依存していないすべてのノードを出力して削除します。それが完了すると、他のいくつかのノードが解放され、プロセスを繰り返すことができます。
追伸初期リスト (子=親) に自己参照テーブルを挿入しないようにしてください。