非常に大規模なOracleデータベースを使用しており、多数のテーブルと数百万の行があります。それらの1つを削除する必要がありますが、それを削除しても、外部キーレコードとしてポイントしている他の依存行が破損しないことを確認したいです。この行を指す他のすべてのレコード、または少なくともテーブルスキーマのリストを取得する方法はありますか?自分で削除して例外をキャッチすることもできることはわかっていますが、自分でスクリプトを実行することはないため、最初からクリーンに実行する必要があります。
OracleのSQL DeveloperツールとAllRoundAutomationsのPL/SQL Developerを自由に利用できます。
前もって感謝します!
私はいつもスターティングテーブルの外部キーを見て、戻ってきました。通常、DBツールには依存関係ノードまたは制約ノードがあります。 PL/SQL DeveloperにFKを表示する方法があることは知っていますが、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';
データベースでSHITABLEを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を生成して、ザップしたい値をこれらのテーブルに照会できます。
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の制約は、テーブルインデックスを使用してデータを参照します。
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;