(私の最初の回答は長すぎて編集が難しくなり、コミュニティ ウィキ化されました。これは本当に面倒です。これがスクリプトの最新バージョンです。)
このスクリプトは、再帰によってカスケード削除を実行しようとします。循環参照がある場合は、無限ループを回避する必要があります。ただし、すべての循環参照制約に ON DELETE SET NULL
が必要です または ON DELETE CASCADE
.
CREATE OR REPLACE PROCEDURE delete_cascade( table_owner VARCHAR2, parent_table VARCHAR2, where_clause VARCHAR2 ) IS /* Example call: execute delete_cascade('MY_SCHEMA', 'MY_MASTER', 'where ID=1'); */ child_cons VARCHAR2(30); parent_cons VARCHAR2(30); child_table VARCHAR2(30); child_cols VARCHAR(500); parent_cols VARCHAR(500); delete_command VARCHAR(10000); new_where_clause VARCHAR2(10000); /* gets the foreign key constraints on other tables which depend on columns in parent_table */ CURSOR cons_cursor IS SELECT owner, constraint_name, r_constraint_name, table_name, delete_rule FROM all_constraints WHERE constraint_type = 'R' AND delete_rule = 'NO ACTION' AND r_constraint_name IN (SELECT constraint_name FROM all_constraints WHERE constraint_type IN ('P', 'U') AND table_name = parent_table AND owner = table_owner) AND NOT table_name = parent_table; -- ignore self-referencing constraints /* for the current constraint, gets the child columns and corresponding parent columns */ CURSOR columns_cursor IS SELECT cc1.column_name AS child_col, cc2.column_name AS parent_col FROM all_cons_columns cc1, all_cons_columns cc2 WHERE cc1.constraint_name = child_cons AND cc1.table_name = child_table AND cc2.constraint_name = parent_cons AND cc1.position = cc2.position ORDER BY cc1.position; BEGIN /* loops through all the constraints which refer back to parent_table */ FOR cons IN cons_cursor LOOP child_cons := cons.constraint_name; parent_cons := cons.r_constraint_name; child_table := cons.table_name; child_cols := ''; parent_cols := ''; /* loops through the child/parent column pairs, building the column lists of the DELETE statement */ FOR cols IN columns_cursor LOOP IF child_cols IS NULL THEN child_cols := cols.child_col; ELSE child_cols := child_cols || ', ' || cols.child_col; END IF; IF parent_cols IS NULL THEN parent_cols := cols.parent_col; ELSE parent_cols := parent_cols || ', ' || cols.parent_col; END IF; END LOOP; /* construct the WHERE clause of the delete statement, including a subquery to get the related parent rows */ new_where_clause := 'where (' || child_cols || ') in (select ' || parent_cols || ' from ' || table_owner || '.' || parent_table || ' ' || where_clause || ')'; delete_cascade(cons.owner, child_table, new_where_clause); END LOOP; /* construct the delete statement for the current table */ delete_command := 'delete from ' || table_owner || '.' || parent_table || ' ' || where_clause; -- this just prints the delete command DBMS_OUTPUT.put_line(delete_command || ';'); -- uncomment if you want to actually execute it: --EXECUTE IMMEDIATE delete_command; -- remember to issue a COMMIT (not included here, for safety) END;
プレ>