Deleting rows from parent and child tables

前端 未结 3 1813
后悔当初
后悔当初 2020-12-14 13:25

Assume two tables in Oracle 10G

TableA (Parent) --> TableB (Child)

Every row in TableA has several child rows related to it in TableB. <

相关标签:
3条回答
  • 2020-12-14 13:45

    Two possible approaches.

    1. If you have a foreign key, declare it as on-delete-cascade and delete the parent rows older than 30 days. All the child rows will be deleted automatically.

    2. Based on your description, it looks like you know the parent rows that you want to delete and need to delete the corresponding child rows. Have you tried SQL like this?

        delete from child_table
            where parent_id in (
                 select parent_id from parent_table
                      where updd_tms != (sysdate-30)
      

      -- now delete the parent table records

      delete from parent_table
      where updd_tms != (sysdate-30);
      

    ---- Based on your requirement, it looks like you might have to use PL/SQL. I'll see if someone can post a pure SQL solution to this (in which case that would definitely be the way to go).

    declare
        v_sqlcode number;
        PRAGMA EXCEPTION_INIT(foreign_key_violated, -02291);
    begin
        for v_rec in (select parent_id, child id from child_table
                             where updd_tms != (sysdate-30) ) loop
    
        -- delete the children
        delete from child_table where child_id = v_rec.child_id;
    
        -- delete the parent. If we get foreign key violation, 
        -- stop this step and continue the loop
        begin
           delete from parent_table
              where parent_id = v_rec.parent_id;
        exception
           when foreign_key_violated
             then null;
        end;
     end loop;
    end;
    /
    
    0 讨论(0)
  • 2020-12-14 13:49

    Here's a complete example of how it can be done. However you need flashback query privileges on the child table.

    Here's the setup.

    create table parent_tab
      (parent_id number primary key,
      val varchar2(20));
    
    create table child_tab
        (child_id number primary key,
        parent_id number,
        child_val number,
         constraint child_par_fk foreign key (parent_id) references parent_tab);
    
    insert into parent_tab values (1,'Red');
    insert into parent_tab values (2,'Green');
    insert into parent_tab values (3,'Blue');
    insert into parent_tab values (4,'Black');
    insert into parent_tab values (5,'White');
    
    insert into child_tab values (10,1,100);
    insert into child_tab values (20,3,100);
    insert into child_tab values (30,3,100);
    insert into child_tab values (40,4,100);
    insert into child_tab values (50,5,200);
    
    commit;
    
    select * from parent_tab
    where parent_id not in (select parent_id from child_tab);
    

    Now delete a subset of the children (ones with parents 1,3 and 4 - but not 5).

    delete from child_tab where child_val = 100;
    

    Then get the parent_ids from the current COMMITTED state of the child_tab (ie as they were prior to your deletes) and remove those that your session has NOT deleted. That gives you the subset that have been deleted. You can then delete those out of the parent_tab

    delete from parent_tab
    where parent_id in
      (select parent_id from child_tab as of scn dbms_flashback.get_system_change_number
      minus
      select parent_id from child_tab);
    

    'Green' is still there (as it didn't have an entry in the child table anyway) and 'Red' is still there (as it still has an entry in the child table)

    select * from parent_tab
    where parent_id not in (select parent_id from child_tab);
    
    select * from parent_tab;
    

    It is an exotic/unusual operation, so if i was doing it I'd probably be a bit cautious and lock both child and parent tables in exclusive mode at the start of the transaction. Also, if the child table was big it wouldn't be particularly performant so I'd opt for a PL/SQL solution like Rajesh's.

    0 讨论(0)
  • 2020-12-14 13:57

    If the children have FKs linking them to the parent, then you can use DELETE CASCADE on the parent.

    e.g.

    CREATE TABLE supplier 
    ( supplier_id numeric(10) not null, 
     supplier_name varchar2(50) not null, 
     contact_name varchar2(50),  
     CONSTRAINT supplier_pk PRIMARY KEY (supplier_id) 
    ); 
    
    
    
    CREATE TABLE products 
    ( product_id numeric(10) not null, 
     supplier_id numeric(10) not null, 
     CONSTRAINT fk_supplier 
       FOREIGN KEY (supplier_id) 
      REFERENCES supplier(supplier_id) 
      ON DELETE CASCADE 
    ); 
    

    Delete the supplier, and it will delate all products for that supplier

    0 讨论(0)
提交回复
热议问题