4

外部キーに基づいて、ルートテーブルのすべての子孫テーブルを検索するクエリを作成できると思いました。

クエリは次のようになります。

select level, lpad(' ', 2 * (level - 1)) || uc.table_name as "TABLE", uc.constraint_name, uc.r_constraint_name
from all_constraints uc
where uc.constraint_type in ('R', 'P')
start with uc.table_name = 'ROOT_TAB'
connect by nocycle prior uc.constraint_name = uc.r_constraint_name
order by level asc;

私が得る結果は次のようになります:

        1 ROOT_TAB XPKROOTTAB  
        1 ROOT_TAB R_20 XPKPART_TAB
        2 CHILD_TAB_1 R_40 XPKROOTTAB
        2 CHILD_TAB_2 R_115 XPKROOTTAB
        2 CHILD_TAB_3 R_50 XPKROOTTAB

この結果は、のすべての子テーブルですが、クエリは、、、またはROOT_TABの子に対して繰り返されません。CHILD_TAB_1CHILD_TAB_2CHILD_TAB_3

再帰クエリは私にとって新しいので、connect by句に何かが欠けていると思いますが、ここでは空白を描画しています。ROOT_TAB単一のクエリでの完全な階層を取得することは実際に可能ですか、それとも再帰的なプロシージャでクエリをラップする方が良いですか?

4

3 に答える 3

8

あなたはこのようなものが欲しいです:

select t.table_name, level,lpad(' ', 2 * (level - 1))||t.table_name 
from user_tables t
join user_constraints c1 
    on (t.table_name = c1.table_name 
    and c1.constraint_type in ('U', 'P'))
left join user_constraints c2 
    on (t.table_name = c2.table_name 
    and c2.constraint_type='R')
start with t.table_name = 'ROOT_TAB'
connect by prior c1.constraint_name = c2.r_constraint_name

元のクエリの問題は、子テーブルのuc.constraint_nameが外部キーの名前であるということです。これは、最初の子をルートテーブルに接続する場合は問題ありませんが、2番目のレベルの子を最初のレベルに接続するために必要なものではありません。そのため、制約に対して2回結合する必要があります。1回はテーブルの主キーを取得するため、もう1回は外部キーを取得するためです。

余談ですが、user_*ビューではなくall_*ビューをクエリする場合は、通常、table_nameだけでなく、table_nameとownerでそれらを結合する必要があります。複数のスキーマに同じ名前のテーブルがある場合、table_nameだけで結合すると、誤った結果が得られます。

于 2010-02-04T22:36:50.860 に答える
3

複数のスキーマと複数のルートテーブルがある場合は、次のようにしてみてください。

WITH constraining_tables AS (SELECT owner, constraint_name, table_name
                               FROM all_constraints
                              WHERE owner LIKE 'ZZZ%' AND constraint_type IN ('U', 'P')),
     constrained_tables AS (SELECT owner, constraint_name, table_name, r_owner, r_constraint_name
                              FROM all_constraints
                             WHERE owner LIKE 'ZZZ%' AND constraint_type = 'R'),
     root_tables AS (SELECT owner, table_name FROM constraining_tables
                     MINUS
                     SELECT owner, table_name FROM constrained_tables)
    SELECT c1.owner || '.' || c1.table_name, LEVEL, LPAD (' ', 2 * (LEVEL - 1)) || c1.owner || '.' || c1.table_name
      FROM    constraining_tables c1
           LEFT JOIN
              constrained_tables c2
           ON c1.owner = c2.owner AND c1.table_name = c2.table_name
START WITH c1.owner || '.' || c1.table_name IN (SELECT owner || '.' || table_name FROM root_tables)
CONNECT BY PRIOR c1.constraint_name = c2.r_constraint_name
于 2012-06-29T18:41:52.043 に答える
0

深く調査した後、すべてのテーブルを処理し、階層内のテーブルの最大レベルを取得する独自のバージョンを作成しました(親子関係のないテーブルも考慮に入れて、すべてのスキーマを読み取り、ルートとともにレベル1になります)もの)。アクセスできる場合は、all_の代わりにdba_テーブルを使用してください。

      WITH hier AS (
                         SELECT child_table owner_table_name
                              , LEVEL lvl
                              , LPAD (' ', 4 * (LEVEL - 1)) || child_table indented_child_table
                              , sys_connect_by_path( child_table, '|' )  tree
                           FROM (
/*----------------------------------------------------------------------*/
/* Retrieve all tables. Set them as the Child column, and set their     */
/* Parent Column to NULL. This is the root list (first iteration)       */
/*----------------------------------------------------------------------*/
                                  SELECT NULL                              parent_table
                                       , a.owner || '.' || a.table_name    child_table
                                    FROM all_tables a
                                   UNION
/*----------------------------------------------------------------------*/
/* List of all possible Parent-Child relations. This table is used as   */
/* a link list, to link the current iteration with the next one, from   */
/* root to last child (last child is what we are interested to find).   */
/*----------------------------------------------------------------------*/
                                  SELECT p.owner   || '.' || p.table_name            parent_table
                                       , c.owner   || '.' || c.table_name            child_table
                                    FROM all_constraints p, all_constraints c
                                   WHERE p.owner || '.' || p.constraint_name = c.r_owner || '.' || c.r_constraint_name
                                     AND (p.constraint_type = 'P' OR p.constraint_type = 'U')
                                     AND c.constraint_type = 'R'
                                )
                     START WITH parent_table IS NULL
/*----------------------------------------------------------------------*/
/* NOCYCLE prevents infinite loops (i.e. self referencing table constr) */
/*----------------------------------------------------------------------*/
                     CONNECT BY NOCYCLE PRIOR child_table = parent_table
                   )
                     SELECT *
                       FROM hier
                      WHERE (owner_table_name, lvl) IN (   SELECT owner_table_name
                                                                , MAX(lvl)
                                                             FROM hier
                                                         GROUP BY owner_table_name
                                                       );

編集:無限ループを見つけるとき、このクエリには「一種の」問題があります。

このツリーがある場合:

b --> c --> d
b <-- c

レベル2をcに次のように割り当てb --> c 、レベル2をbに次のように割り当てます。c --> b

dの場合、検出するb --> c --> d ため、レベル3が割り当てられます。

ご覧のとおり、問題はループの内側にあり、外側からの値は常に最大の正しいレベルになります

于 2017-04-29T20:18:42.987 に答える