Oracleで1つのレコードを指している外交キーの依存関係を見つける方法は?

StackOverflow https://stackoverflow.com/questions/2509512

質問

非常に大きなOracleデータベースがあり、多くのテーブルと数百万の行があります。それらのいずれかを削除する必要がありますが、それをドロップすることで、外国のキーレコードとして指す他の依存行を壊さないようにしたいと思います。この行を指す他のすべてのレコード、または少なくともテーブルスキーマのリストを取得する方法はありますか?自分で削除して例外をキャッチしようとすることができることを知っていますが、スクリプトを自分で実行して、最初にクリーンを実行するために必要なことはありません。

OracleのTools SQL開発者と、AllRoundAutomationsのPL/SQL開発者が自由に使用されています。

前もって感謝します!

役に立ちましたか?

解決

私はいつもスタートテーブルの外国の鍵を見て、私の道を進んでいます。 DBツールには通常、依存関係または制約ノードがあります。 PL/SQL開発者にはFKを見る方法があることは知っていますが、使用してからしばらく経ちましたので、説明できません...

xxxxxxxxxxxxをテーブル名に置き換えるだけです...

/* The following query lists all relationships */ 

select
 a.owner||'.'||a.table_name "Referenced Table"
,b.owner||'.'||b.table_name "Referenced by"
,b.constraint_name "Foreign Key"
from all_constraints a, all_constraints b 
where 
b.constraint_type = 'R'
and a.constraint_name = b.r_constraint_name 
and b.table_name='XXXXXXXXXXXX' -- Table name 
order by a.owner||'.'||a.table_name

他のヒント

テーブルへのすべての参照をリストするための私のソリューションは次のとおりです。

select
  src_cc.owner as src_owner,
  src_cc.table_name as src_table,
  src_cc.column_name as src_column,
  dest_cc.owner as dest_owner,
  dest_cc.table_name as dest_table,
  dest_cc.column_name as dest_column,
  c.constraint_name
from
  all_constraints c
inner join all_cons_columns dest_cc on
  c.r_constraint_name = dest_cc.constraint_name
  and c.r_owner = dest_cc.owner
inner join all_cons_columns src_cc on
  c.constraint_name = src_cc.constraint_name
  and c.owner = src_cc.owner
where
  c.constraint_type = 'R'
  and dest_cc.owner = 'MY_TARGET_SCHEMA'
  and dest_cc.table_name = 'MY_TARGET_TABLE'
  --and dest_cc.column_name = 'MY_OPTIONNAL_TARGET_COLUMN'
;

このソリューションを使用すると、どの列がターゲットテーブルのどの列を参照しているかについての情報もあります(そして、それをフィルタリングできます)。

私は最近同様の問題を抱えていましたが、すぐに経験しましたが、直接的な依存関係を見つけるだけでは十分ではありません。そこで、マルチレベルの外部キー依存関係のツリーを表示するためにクエリを書きました。

SELECT LPAD(' ',4*(LEVEL-1)) || table1 || ' <-- ' || table2 tables, table2_fkey
FROM
  (SELECT a.table_name table1, b.table_name table2, b.constraint_name table2_fkey
  FROM user_constraints a, user_constraints b 
  WHERE a.constraint_type IN('P', 'U') 
  AND b.constraint_type = 'R' 
  AND a.constraint_name = b.r_constraint_name 
  AND a.table_name != b.table_name
  AND b.table_name <> 'MYTABLE')
CONNECT BY PRIOR  table2 = table1 AND LEVEL <= 5
START WITH table1 = 'MYTABLE';

私のデータベースでmyTableとして出荷を使用する場合、次のような結果が得られます。

SHIPMENT <-- ADDRESS
SHIPMENT <-- PACKING_LIST
    PACKING_LIST <-- PACKING_LIST_DETAILS
    PACKING_LIST <-- PACKING_UNIT
        PACKING_UNIT <-- PACKING_LIST_ITEM
    PACKING_LIST <-- PO_PACKING_LIST
...

データ辞書を使用して、問題のテーブルの主要なキーを参照するテーブルを識別できます。それから、いくつかのダイナミックSQLを生成して、ZAPにする値をこれらのテーブルをクエリすることができます。

SQL> declare
  2      n pls_integer;
  3      tot pls_integer := 0;
  4  begin
  5      for lrec in ( select table_name from user_constraints
  6                    where r_constraint_name = 'T23_PK' )
  7      loop
  8          execute immediate 'select count(*) from '||lrec.table_name
  9                              ||' where col2 = :1' into n using &&target_val;
 10          if n = 0 then
 11              dbms_output.put_line('No impact on '||lrec.table_name);
 12          else
 13              dbms_output.put_line('Uh oh! '||lrec.table_name||' has '||n||' hits!');
 14          end if;
 15          tot := tot + n;
 16      end loop;
 17      if tot = 0
 18      then
 19          delete from t23 where col2 = &&target_val;
 20          dbms_output.put_line('row deleted!');
 21      else
 22          dbms_output.put_line('delete aborted!');
 23      end if;
 24  end;
 25  /
Enter value for target_val: 6
No impact on T34
Uh oh! T42 has 2 hits!
No impact on T69
delete aborted!

PL/SQL procedure successfully completed.

SQL>

この例は少しチートします。ターゲットプライマリキーの名前はハードコードされており、参照列はすべての従属テーブルで同じ名前を持っています。これらの問題を修正することは、読者のための演習として残されています;)

同様の状況がありました。私の場合、私はいくつかのレコードを持っていましたが、それは同じIDで、ケースによってのみ異なるものになりました。どちらが削除/更新するのが最も簡単かを知るために、それぞれに依存レコードが存在するものを確認したかった

次のように、各テーブル/マスターレコードの組み合わせにカウントされた子供のテーブルごとに指定された記録を指すすべての子供レコードを印刷します

declare
  --
  -- Finds and prints out how many children there are per table and value for each value of a given field
  --

  -- Name of the table to base the query on  
  cTable      constant varchar2(20) := 'FOO';
  -- Name of the column to base the query on
  cCol        constant varchar2(10) := 'ID';
  -- Cursor to find interesting values (e.g. duplicates) in master table
  cursor cVals is
    select id 
    from foo f
    where exists (  select 1 from foo f2 
                    where upper(f.id) = upper(f2.id)
                    and f.rowid != f2.rowid );

  -- Everything below here should just work
  vNum        number(18,0);
  vSql        varchar2(4000);

  cOutColSize   number(2,0) := 30;

  cursor cReferencingTables is
    select
      consChild.table_name,
      consChild.constraint_name,
      colChild.column_name     
    from user_constraints consMast
    inner join user_constraints consChild on consMast.constraint_name = consChild.r_constraint_name  
    inner join USER_CONS_COLUMNS colChild on consChild.CONSTRAINT_NAME = colChild.CONSTRAINT_NAME
    inner join USER_CONS_COLUMNS colMast on colMast.CONSTRAINT_NAME = consMast.CONSTRAINT_NAME
    where consChild.constraint_type = 'R'      
      and consMast.table_name = cTable
      and colMast.column_name = cCol
    order by consMast.table_name, consChild.table_name;
begin

  dbms_output.put_line(
    rpad('Table', cOutColSize) || 
    rpad('Column', cOutColSize) || 
    rpad('Value', cOutColSize) || 
    rpad('Number', cOutColSize)
  );
  for rRef in cReferencingTables loop
    for rVals in cVals loop
      vSql := 'select count(1) from ' || rRef.table_name || ' where ' || rRef.column_name || ' = ''' || rVals.id || '''';
      execute immediate vSql into vNum;
      if vNum > 0 then
        dbms_output.put_line(
          rpad(rRef.table_name, cOutColSize) || 
          rpad(rRef.column_name, cOutColSize) || 
          rpad(rVals.id, cOutColSize) || 
          rpad(vNum, cOutColSize) );
      end if;
    end loop;
  end loop;
end;

外国のキー関係に基づいて、テーブルの依存関係の順序を見つけることがどれほど難しいかに驚きました。すべてのテーブルからデータを削除して再度インポートしたいので、必要でした。これが、依存関係の順序でテーブルをリストするために書いたクエリです。以下のクエリを使用して削除をスクリプト化し、クエリの結果を逆順に再度インポートすることができました。

   SELECT referenced_table
         ,MAX(lvl) for_deleting
         ,MIN(lvl) for_inserting
   FROM
         ( -- Hierarchy of dependencies
         SELECT LEVEL lvl
               ,t.table_name referenced_table
               ,b.table_name referenced_by
         FROM user_constraints A
         JOIN user_constraints b 
               ON  A.constraint_name = b.r_constraint_name
               and b.constraint_type = 'R'
         RIGHT JOIN user_tables t
               ON  t.table_name = A.table_name
         START WITH b.table_name IS NULL
         CONNECT BY b.table_name = PRIOR t.table_name
         )
   GROUP BY referenced_table
   ORDER BY for_deleting, for_inserting;

Oracle Constraintsは、テーブルインデックスを使用してデータを参照します。
テーブルが1つのテーブルを参照しているテーブルを確認するには、逆の順序でインデックスを探してください。

/* Toggle ENABLED and DISABLE status for any referencing constraint: */ 

select 'ALTER TABLE '||b.owner||'.'||b.table_name||' '||
        decode(b.status, 'ENABLED', 'DISABLE ', 'ENABLE ')||
       'CONSTRAINT '||b.constraint_name||';' 
  from all_indexes a,
       all_constraints b
 where a.table_name='XXXXXXXXXXXX' -- Table name 
   and a.index_name = b.r_constraint_name;

OBS。:参照を無効にすると、DMLコマンドの時間が大幅に改善されます(更新、削除、挿入)。

これは、すべてのデータが一貫していることがわかっているバルク操作で多くの役立ちます。

/* List which columns are referenced in each constraint */

select ' TABLE "'||b.owner||'.'||b.table_name||'"'||
        '('||listagg (c.column_name, ',') within group (order by c.column_name)||')'|| 
        ' FK "'||b.constraint_name||'" -> '||a.table_name||
        ' INDEX "'||a.index_name||'"'
        "REFERENCES"
  from all_indexes a,
       all_constraints b,
       all_cons_columns c
 where rtrim(a.table_name) like 'XXXXXXXXXXXX' -- Table name 
   and a.index_name = b.r_constraint_name
   and c.constraint_name = b.constraint_name
 group by b.owner, b.table_name, b.constraint_name, a.table_name, a.index_name
 order by 1;
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top