How to generate DELETE statements in PL/SQL, based on the tables FK relations?

前端 未结 3 965
日久生厌
日久生厌 2020-12-15 01:28

Is it possible via script/tool to generate authomatically many delete statements based on the tables fk relations, using Oracle PL/SQL?

In example: I have the table:

3条回答
  •  天命终不由人
    2020-12-15 01:56

    (My first answer became too long and difficult to edit, and it got Community Wikified, which is really annoying. Here is the latest version of the script.)

    This script attempts to perform a cascading delete through recursion. It should avoid infinite loops when there are circular references. But it requires that all circular referential constraints have ON DELETE SET NULL or 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;
    

提交回复
热议问题