views:

677

answers:

3

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: CHICKEN (CHICKEN_CODE NUMBER) and there are 30 tables with fk references to its CHICKEN_CODE that I need to delete; there are also other 150 tables foreign-key-linked to that 30 tables that I need to delete first.

Is there some tool/script PL/SQL that I can run in order to generate all the necessary delete statements based on the FK relations for me?

(by the way, I know about cascade delete on the relations, but please pay attention: I CAN'T USE IT IN MY PRODUCTION DATABASE, because it's dangerous!)

I'm using Oracle DataBase 10G R2.

Please pay attention to this:

http://stackoverflow.com/questions/485581/generate-delete-statement-from-foreign-key-relationships-in-sql-2008/2677145#2677145

Another user has just written it in SQL SERVER 2008, anyone is able to convert to Oracle 10G PL/SQL? I am not able to... :-(

Please assume that V_CHICKEN and V_NATION are the criteria to select the CHICKEN to delete from the root table: the condition is: "where COD_CHICKEN = V_CHICKEN AND COD_NATION = V_NATION" on the root table.

+1  A: 

This is a great exercise in developing your PL/SQL skills and general Oracle knowledge!

You need to identify all constrained columns in all tables with relations descending from your master table. You can get all the information you need from two views: ALL_CONSTRAINTS and ALL_CONS_COLUMNS. (If all the tables are in the same schema as the user executing the script, you can use USER_CONSTRAINTS and USER_CONS_COLUMNS if you prefer)

This query will find all the foreign key constraints which refer back to a given table (CUSTOMER in this example):

SELECT constraint_name, table_name, constraint_type
  FROM all_constraints
 WHERE constraint_type = 'R'
   AND r_constraint_name IN (SELECT constraint_name
                               FROM all_constraints
                              WHERE constraint_type IN ('P', 'U')
                                AND table_name = 'CUSTOMER');


CONSTRAINT_NAME                C
------------------------------ -
CUSTOMER_FK1                   R
CUSTOMER_FK4                   R
CUSTOMER_FK5                   R
CUSTOMER_FK3                   R
CUSTOMER_FK2                   R

Now, for each of the results from that query, you can use the CONSTRAINT_NAME column to get a table and column name which you can use to write DELETE statements to delete all child rows in all child tables.

This example gets the table and column name for a constraint called CUSTOMER_FK1

SELECT table_name, column_name
  FROM user_cons_columns
 WHERE constraint_name = 'CUSTOMER_FK1'

TABLE_NAME                    COLUMN_NAME                       
----------------------------- ------------------------------------
RESERVATION                   CUSTOMER_UID

So you could do, for example:

DELETE FROM reservation
 WHERE customer_uid = 00153464

or

DELETE FROM reservation
 WHERE customer_uid IN (SELECT customer_uid
                          FROM customer
                         WHERE customer_type = 'X')

But your child tables also have child tables, so of course you will have to delete those child rows (call them grandchild rows) first. Supposing there is a table called reservation_detail which has a foreign key relationship with reservation, your delete command for reservation_detail might look like:

DELETE FROM reservation_detail 
 WHERE reservation_uid in (SELECT reservation_uid     
                             FROM reservation 
                            WHERE customer_uid IN (SELECT customer_uid
                                                     FROM customer
                                                    WHERE customer_type = 'X')

And if reservation_detail also has children... you get the idea. Of course you could use joins instead of nested queries, but the principle is the same: the more levels deep your dependencies go, the more complex your delete commands become.

So now you know how to do it, the challenge is to write a generic PL/SQL script to delete all child rows, grandchild rows, great-grandchild rows ... (ad infinitum) for any given table, from the bottom up. You will have to employ recursion. Should be a fun program to write!

(Last edit: removed the script; see my other answer for the final solution.)

Charles
Please help! I've tried to write the procedure without success. I need that procedure within a few days... My referential integrities are 5 pages long, A3 size.... help!!
The chicken in the kitchen
When I get some time I'll write you some code. Meanwhile, try google... somebody must have written this already.
Charles
I am trying with Google since 3 or 4 days without interesting results.... Of course I have always typed the wrong keywords... I have tried with: code, generation, delete statement, referential integrity, dba_constraints, and so on... I should be grateful if you could help me, because I am not able to complete this task. In the meantime, I'll continue to search with Google.
The chicken in the kitchen
I'd try: oracle, pl/sql, delete, table, data, children, recursive, recursion. I had a quick look and didn't see anything obvious though.
Charles
By the way, the script I've provided above can potentially open a lot of cursors if you have relationships many levels deep. Depending on your server's settings, you could hit the OPEN_CURSORS limit.
Charles
First of all, thank you very much for your kind help! ;-) Now I am trying the script, please note that the referential integrities propagate also in other 5 schemas... I hope that the script handles this case, too.
The chicken in the kitchen
The number of the records is increasing and increasing, but the DISTINCT delete statements are only 39..... Have I met a loop???
The chicken in the kitchen
9527 records written in the RESULT_TABLE (I've written the delete statements in a temporary table, instead of displaying them on the screen with dbms_output.put_line), but the distinct records are 39, and it's running now...
The chicken in the kitchen
I have stopped the running script, in the referential integrities there are loops, how can I handle them???
The chicken in the kitchen
I have 3 delete rules on the referential integrities: CASCADE, NO ACTION and SET NULL. I've modified the CURSOR fetching only NO ACTION referential integrities, is it correct? CURSOR cons_cursor IS (....omissis....) AND delete_rule = 'NO ACTION';I've noted that the LOOPS have always the CASCADE option.
The chicken in the kitchen
I have just noted that the NESTED conditions will be lost: it will be kept only the condition of level 1.
The chicken in the kitchen
I'm not sure what you're telling me... However, I have realised that my script is no good. It would delete *everything* from all the child tables... Stupid; that's what happens when I code in a hurry. :( Sorry, but this is a tough one. I doubt I'm going to have time to crack it.
Charles
Don't worry friend, it's all day that I am studying your program... ;-) It's 90% perfect, but it does not handle the where conditions when invoking recursion: it only keeps the "surface" where condition. I have not found the solution, I have only thought to a second parameter that is a string incremented ad every recursion... but now I am having problems with "PLS-00306: wrong number or types of arguments in call to 'DELETE_CASCADE'", and I don't know why..... Help!! :-)
The chicken in the kitchen
Right - every DELETE includes a subquery in its **where** clause. What I forgot was, that subquery also needs a **where** clause: it must only delete the rows which are children of the rows *to be deleted* from its parent table. To build this **where** clause, we need to know which rows we will delete from the parent table, but to know that we also need to know which rows will be deleted from the parent's parent... and so on. So, from the top (master table) down the tree, each delete will have multiple nested subqueries (the lower you go, the more nested subqueries). It could get ugly.
Charles
If you're getting PLS-00306, it must be caused by your change to the script... somehow you're calling it without passing in all the parameters. (I don't think it was possible to get this error with my original script)
Charles
I have turned the stored procedure into a function, so it will be easier to merge the strings with recursive calls.... Now I am running it again, but I am not so satisfied for what am I seeing... (help)
The chicken in the kitchen
Above I have posted the code of the stored function...
The chicken in the kitchen
I've fixed my script. Good luck. :)
Charles
Tomorrow morning, when I'll come back to my office, I'll try again your script, I'm 99% sure it works fine! ;-) Thank you again for your kind, prompt, fast help!! ;-)
The chicken in the kitchen
I hope it works... I will be really impressed if it does!
Charles
Charles, now I am running your new release..... :-)
The chicken in the kitchen
Of course, instead of EXECUTE IMMEDIATE your command, I store it in a table called RIS, in order to avoid problems ;-)
The chicken in the kitchen
I have added the condition:AND DELETE_RULE = 'NO ACTION'in order to avoid deletion in case of referential integrities of type 'CASCADE' and of type 'SET NULL'.Do you agree??
The chicken in the kitchen
CURSOR cons_cursor IS SELECT owner, constraint_name, r_constraint_name, table_name FROM all_constraints WHERE constraint_type = 'R' 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 DELETE_RULE = 'NO ACTION';
The chicken in the kitchen
Your script is PERFECT, but my idea to add the code:AND DELETE_RULE = 'NO ACTION'is WRONG. Now I run the script again, without my added code.In my database there are some tables that reference themselves, with the same fields......
The chicken in the kitchen
Oh, no, now I obtain the following error!! :-(ORA-01461: can bind a LONG value only for insert into a LONG column
The chicken in the kitchen
The problem are the referential integrities auto-referential, as I have written above... How can I detect loops??
The chicken in the kitchen
SELECT owner, constraint_name, r_constraint_name, table_name FROM all_constraints WHERE constraint_type = 'R' 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 table_name != parent_table;I have added the condition TABLE_NAME != PARENT_TABLE to avoid LOOPS...
The chicken in the kitchen
Now I re-run all!! :-)
The chicken in the kitchen
My condition TABLE_NAME != PARENT_TABLE avoids ONLY the loops of depth = 1. I have not only cases as follows:TABLE A => REFERENTIAL INTEGRITY => TABLE A... but I have also cases as follows:TABLE A => TABLE B => TABLE C => TABLE D => TABLE A... how can I fix this??? HELP!
The chicken in the kitchen
Oh boy, circular references. This is not good! Let me think about it...
Charles
I'm assuming your circular references are based on Unique constraints on nullable columns. This means the script will also need to generate UPDATE statements to set the parent columns to NULL before the parent rows can be deleted... Not the mention the loop condition it needs to handle.
Charles
Now I try to copy the creation script of a table with circular references, in order to show you the critical situation.
The chicken in the kitchen
Are your constraints deferrable? Do `select * FROM all_constraints` and look at the DEFERRABLE column of the constraints on all of your tables.
Charles
ALTER TABLE CIRCLE ADD ( CONSTRAINT FK000001 FOREIGN KEY (COD_CHICKEN, COD_NATION, NUMERICAL_CODE) REFERENCES MONSTER (COD_CHICKEN,COD_NATION, NUMERICAL_CODE) ON DELETE SET NULL, CONSTRAINT FK000002 FOREIGN KEY (COD_TRTT, NUM_ANN_SRI, TIP_FORM_TRTT) REFERENCES TOWER (COD_TRTT,NUM_ANN_SRI,TIP_FORM_TRTT) ON DELETE SET NULL, CONSTRAINT FK000003 FOREIGN KEY (COD_CHICKEN1) REFERENCES OCTOPUS (COD_CHICKEN) ON DELETE SET NULL, CONSTRAINT FK000004 FOREIGN KEY (COD_BRK) REFERENCES BRIDGE (COD_BRK) ON DELETE SET NULL,(... CONTINUE ...)
The chicken in the kitchen
CONSTRAINT FK000005 FOREIGN KEY (TIP_CIRCLE) REFERENCES TYPE_CIRCLE (TIP_CIRCLE), CONSTRAINT FK000006 FOREIGN KEY (COD_CHICKEN_TYPE) REFERENCES TYPE_MODE (COD_CHICKEN_TYPE), CONSTRAINT FK000007 FOREIGN KEY (COD_DVS) REFERENCES DVD_CDROM (COD_DVD), CONSTRAINT FK000008 FOREIGN KEY (COD_AFF) REFERENCES AFFERMATION (COD_AFFERMATION) ON DELETE SET NULL, CONSTRAINT FK000009 FOREIGN KEY (COD_CHICKEN, COD_NATION, COD_CIRCLE, COD_VRN_CIRCLE) REFERENCES CIRCLE (COD_CHICKEN,COD_NATION,COD_CIRCLE,COD_VRN_CIRCLE) ON DELETE CASCADE);
The chicken in the kitchen
My constraints are NOT deferrable :-(
The chicken in the kitchen
ALWAYS not deferrable.
The chicken in the kitchen
I am not authorized to make them deferrable (of course).
The chicken in the kitchen
Is FK000009 on CIRCLE or a different table? If it is on CIRCLE, wouldn't it be completely pointless? How can a constraint reference the same columns in its own table?
Charles
If all of the circular constraints have ON DELETE SET NULL, it would really help. From the example you posted above, it looks like that might be the case.
Charles
I don't know how can a constraint reference the same columns of the same table, but in production environment, so it is :-)Please assume that all of the circular constraints have ON DELETE SET NULL.
The chicken in the kitchen
I am not sure that all the circular constraints have ON DELETE SET NULL, please assume so...I have difficult in writing the creation scripts, because I can't publish the real names (of course).
The chicken in the kitchen
Now I try to post another example, so you can see if our assumption (on delete set null) is correct.
The chicken in the kitchen
ALTER TABLE AUTO_MOTO_PRP ADD ( CONSTRAINT FK000013 FOREIGN KEY (COD_CHICKEN, COD_PRP, PROGRESS_CODE_PRP) REFERENCES MOV_PRP (COD_CHICKEN,COD_PRP,PROGRESS_CODE_PRP), CONSTRAINT FK000014 FOREIGN KEY (COD_CHICKEN, COD_LIV_OGZ) REFERENCES LIV_OGZ (COD_COMP_PTF,COD_LIV_OGZ), CONSTRAINT FK000015 FOREIGN KEY (COD_CHICKEN, COD_GRU_ARZ) REFERENCES GRU_ARZ (COD_CHICKEN,COD_GRU_ARZ) ON DELETE SET NULL, CONSTRAINT FK000016 FOREIGN KEY (COD_CHICKEN0, COD_NATION, PROGRESS_CODE) REFERENCES AUTO_MOTO (COD_CHICKEN,COD_NATION,PROGRESS_CODE) ON DELETE SET NULL);
The chicken in the kitchen
ALTER TABLE AUTO_MOTO ADD ( CONSTRAINT FK000022 FOREIGN KEY (COD_CHICKEN, COD_NATION, PROGRESS_CODE) REFERENCES MOV (COD_CHICKEN,COD_NATION,PROGRESS_CODE), CONSTRAINT FK000023 FOREIGN KEY (COD_CHICKEN, COD_LIV_OGZ) REFERENCES LIV_OGZ (COD_COMP_PTF,COD_LIV_OGZ), CONSTRAINT FK000024 FOREIGN KEY (COD_CHICKEN, COD_GRU_ARZ) REFERENCES GRU_ARZ (COD_CHICKEN,COD_GRU_ARZ) ON DELETE SET NULL, CONSTRAINT FK000025 FOREIGN KEY (COD_CHICKEN0, COD_PRP, PROGRESS_CODE_PRP) REFERENCES AUTO_MOTO_PRP (COD_CHICKEN,COD_PRP,PROGRESS_CODE_PRP) ON DELETE SET NULL);
The chicken in the kitchen
In this last case, we have two crossed tables, which reference themselves.
The chicken in the kitchen
I've added a new version of the script. IF all of your circular constraints have ON DELETE SET NULL, this should prevent the infinite looping problem. Try it and let's find the next problem... :)
Charles
Oh, I forgot about the self-referencing constraints. You will probably have to modify the cursor for those again.
Charles
I try immediately!! :-) I add again the condition:TABLE_NAME != PARENT_TABLEas before.
The chicken in the kitchen
I have tried to pass the critical tables, without starting from the root. The procedure doesn't loop (!!!). Now I make the global run, and then try to launch it in test environment (don't worry per production data).
The chicken in the kitchen
GLOBAL RUN IS RUNNING! :-)
The chicken in the kitchen
Something went wrong: the critical tables have NOT a DELETE STATEMENT... Is it normal? Can I try in test environment?
The chicken in the kitchen
I just added `AND delete_rule = 'NO ACTION'`, this is simpler.
Charles
Are you saying there is no delete statement being generated for some tables? Maybe my change caused that... but my head is going to implode trying to figure out why!
Charles
ORA-01407: cannot update ("PROPRIETOR"."CIRCLE"."COD_CHICKEN") to NULLORA-06512: at line 4490
The chicken in the kitchen
Of course, CIRCLE is the self-integrity-referenced table...I substitute TABLE_NAME != PARENT_TABLE with your suggestion:AND DELETE_RULE = 'NO ACTION'.
The chicken in the kitchen
ORA-01407 means you have a constraint with ON DELETE SET NULL, but the constrained column is a NOT NULL column. That would be a ridiculous situation!
Charles
I know... but I have not designed the database ;-)With your suggestion, the procedure is running now...
The chicken in the kitchen
Now it is much slower... be patient...
The chicken in the kitchen
(the gold commentator badge is of course mine :-) )
The chicken in the kitchen
44 delete statements written till now
The chicken in the kitchen
Uh oh.. that's insanely slow!
Charles
Don't worry... The only problem is that I don't see the critical tables as delete statement...
The chicken in the kitchen
77 delete statements written..... it is running....
The chicken in the kitchen
Remember it won't delete rows from any table until ALL dependants have been deleted. Maybe it hasn't got that far yet.
Charles
It is normal that it is insanely slow, because the printed graph of the referential integrities fulls 5 pages A3 size!
The chicken in the kitchen
Without the self-referenced-integrities, we have solved the problem! But in my case, we have them.....
The chicken in the kitchen
111 delete statements written.............
The chicken in the kitchen
The self-referencing constraints are still causing trouble? That means some of them must be NO ACTION. We need to modify the cursor to ignore self-referencing constraints.
Charles
I don't see any cycle in the delete statement: all the delete statement obtained are UNIQUE.
The chicken in the kitchen
If it goes into a loop on a self-referencing constraint, put `and table_name <> parent_table` back into the query.
Charles
The only problem is that now the procedure does NOT enter in the loop, it avoids them at all... FINISHED!!! It has finished running! Now I copy the delete statements to UltraEdit, transform them in a PL/SQL block, and then try to run.
The chicken in the kitchen
133 delete statements at all.
The chicken in the kitchen
How many tables are there? You should have at least that many delete statements, probably many more.
Charles
I am sorry :-(ORA-01407: cannot update ("PROPRIETOR"."CIRCLE"."COD_CHICKEN") to NULLORA-06512: at line 1302I state that a delete statement for the CIRCLE table does not exist in the output of the procedure.
The chicken in the kitchen
I can see that the procedure does NOT enter in the loops, because the tables involved in the loop are not present. It avoids the loops without running on them for only one time.
The chicken in the kitchen
The FK on CIRCLE tables give problems...
The chicken in the kitchen
It is completely insane to have an ON DELETE SET NULL constraint for a NOT NULL column! That means you MUST delete the child before the parent, which is impossible if you're dealing with a circular reference where the "parent" is really the child (or descendant) of the "child". The only way you could ever delete the "parent" of CIRCLE is if you have a strange series or constraints like this : `C -> B -> A -> C` where ALL of the constraints are ON DELETE SET NULL, and the C->B and B->A constraints are on nullable columns.
Charles
(cont) But then you would have delete in this order: A, C, B. (In this example A is CIRCLE, and C is the table which is throwing the ORA-01407). If this is how your database is set up, I don't think I have the time or energy to write a script to handle it. I suggest you talk to the DBA about removing these silly FK's. Sorry :(
Charles
IF your database is set up like I described above (which seems unlikely), then you should be able to do all the deletes with TWO passes. Ignore all the exceptions in the first pass (but continue), and then the second pass would handle all those tables.
Charles
Do you suggest to run your procedure two times, is it right?
The chicken in the kitchen
My problem is that I cannot commit during the execution: at the end of the procedure, the executor can decide whether commit or rollback.
The chicken in the kitchen
Don't let my procedure excecute any commands; I was thinking you could take the output from my procedure (the series of DELETEs) and execute it all twice, continuing past any exceptions. But I'm not sure if it would work, and it requires that your constraints are set up in the odd way I described above.
Charles
Incredible! I have solved in this way: I have run the procedure for the sub-table which gives problem, and then I have obtained a few DELETE statements which I have manually inserted BEFORE the critical point in the script with 133 delete statements: IT WORKS!! :-)
The chicken in the kitchen
PERHAPS, there are two types of the auto-referential integrities constraints: the first type has CASCADE DELETE, and it solves itself. The second type has ON DELETE SET NULL, and is NOT autoresolving. The solution is to run your PERFECT procedure in the sub-loop, and then manually insert the few delete statements, so obtained, in the TOTAL script.
The chicken in the kitchen
I must observe, as you suggest, that in my database there are NONSENSE constraints, this means that I have ON DELETE SET NULL with reference to a NOT-NULLABLE field (COD_CHICKEN). Absurd.
The chicken in the kitchen
Well done!! Now find out who designed your database and ask them what the heck they were thinking. :)
Charles
ah ah ah :-) You're right!!!
The chicken in the kitchen
+1  A: 

The problem is if the top level key column isn't propagated all the way down to the bottom. If you can do DELETE FROM grandchild WHERE parent_id = :1, it is fine. If you have to do,

DELETE FROM grandchild
WHERE child_id in (SELECT id FROM child WHERE parent_id = :1)

then going down six or seven deep will give you ugly (and probably slow) queries.

While you said you can't make the constraints CASCADE, can you make them deferrable initally immediate ? That way existing code should not be impacted. Your 'delete' session would make all constraints deferred. Then delete from the parent, delete from the child where the record wasn't in the parent, delete from the grandchild where there's no match in the child etc...

Gary
I cannot make the constraints CASCADE, because I cannot modify the database: I can only INSERT, DELETE, SELECT and UPDATE, without modifying the database using Data Definition Language statements.
The chicken in the kitchen
+2  A: 

(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;

Now I just need to rewrite it to use joins instead of nested subqueries (don't ask me why I didn't do it that way in the first place).

Charles
Chicken: if this has been any help at all, I would appreciate an upvote; maybe even accepted answer? :) (On *this* answer, as I cannot gain any rep for the first one because it is now community wiki).
Charles
VoteUp requires 15 reputation, so I can't NOW give you an upvote... but in the future, of course ;-)
The chicken in the kitchen
I have clicked to accept the answer.
The chicken in the kitchen
I won the scolar bronze badge for accepting your answer ;-)
The chicken in the kitchen
I wish I could see your database so I could (maybe) get this thing to work for you!
Charles
Imagine the output of your stored procedure: it is composed by 145 DELETE statements! :-)The maximum length of the DELETE statements is: 1556 characters! :-)
The chicken in the kitchen
OF COURSE, I'll vote UP for your answer: I have only to wait for the 15th reputation (at present, my reputation score is 13).
The chicken in the kitchen
NOTE: My boss asked to me to write MANUALLY those 145 delete statements........... This is why I have asked for help!!! :-)
The chicken in the kitchen
I think you are the first for having written such a useful stored procedure!! :-) All databases have referential integrities, and one common problem is often to delete old records respecting the foreign keys, and avoiding to leave "dirty data" on the DB!
The chicken in the kitchen
MICROSOFT WORD statistics of the output of the stored procedure:Pages: 25Words: 7860Characters (no spaces): 68745Characters (with spaces): 76.460Paragraphs: 141Lines: 1011
The chicken in the kitchen
1556 characters, wow :) How many levels deep is it (how many ")" are at the end of the statement)? I have learned an important lesson from this... If I ever design a large database, I think I'll make all constraints deferrable. :)
Charles
The DELETE statement with length of 1556 characters ends with six consecutive right parenthesis )))))):-)
The chicken in the kitchen
Finally I can vote up your USEFUL answer, my reputation score is now 23 and no more 13 :-)
The chicken in the kitchen
I won Supporter bronze badge voting up your question! ;-)
The chicken in the kitchen
Those long (6 level) deletes might take quite a while to run if the tables are large (depends on which indexes exist of course). Have you run the deletes on your production DB yet?
Charles
I recommend editing your question to make it simpler now - maybe remove the code samples and just leave the question text so anyone reading it in future won't be confused.
Charles
I should have used joins instead of nested subqueries... maybe that can be version 2.0. :)
Charles
Hello Charles, the 6-level-deletes are really fast, because they involve a few records. The most nested condition is related to only one row, and is a condition that uses an index built on the two fields of the condition: this rows references to the child tables involving not more than 30-40 records totally.
The chicken in the kitchen
I follow your suggestion, editing the question leaving OUT my WRONG examples ;-)
The chicken in the kitchen
I think that your solution with nested subqueries is more and more readable than the solution with joins ;-)
The chicken in the kitchen
If you mean the output, I think you're right about that. The delete statements with nested queries are very easy to read (when formatted nicely). But huge JOINs with all those ANDs in them would be really ugly!
Charles
I use "Formatter Plus" by Quest Software, in order to format nicely the queries ;-)
The chicken in the kitchen
Me too (in Toad) :)
Charles
I use Toad for Oracle Xpert 10.1.1.8 :-)
The chicken in the kitchen
Charles, could you help me with my second question related to SQL Plus? http://stackoverflow.com/questions/2735942/export-as-insert-statements-but-in-sql-plus-the-line-overrides-2500-characters
The chicken in the kitchen