从父表和子表中删除行

时间:2011-03-04 16:06:40

标签: sql oracle

假设Oracle 10G中有两个表

TableA (Parent) --> TableB (Child)

TableA中的每一行都有几个与TableB相关的子行。

我想删除TableA中的特定行,这意味着我必须首先删除tableB中的相关行。

这会删除子条目

delete from tableB where last_update_Dtm = sysdate-30;

要删除子表中刚刚删除的行的父行,我可以执行类似这样的操作

Delete from TableA where not exists (select 1 from tableB where tableA.key=tableB.key);

以上内容还将删除子表中的行(last_update_Dtm = sysdate-30)为false。 TableA没有last_update_dtm列,因此如果没有子表中的条目,则无法知道要删除哪些行。

我可以在删除之前将密钥保存在子表中,但这似乎是一种昂贵的方法。删除两个表中的行的正确方法是什么?

修改

为了更好地解释我想要实现的目标,如果两个表之间没有约束,以下查询将完成我想要做的事情。

Delete from tableA
Where exists (
Select 1 from tableB
where tableA.key=tableB.key
and tableB.last_update_dtm=sysdate-30)

Delete from tableB where last_update_dtm=systdate-30

3 个答案:

答案 0 :(得分:8)

两种可能的方法。

  1. 如果您有外键,请将其声明为on-delete-cascade并删除超过30天的父行。所有子行都将自动删除。

  2. 根据您的描述,您似乎知道要删除的父行,并且需要删除相应的子行。你有没有尝过这样的SQL?

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

    - 现在删除父表记录

    delete from parent_table
    where updd_tms != (sysdate-30);
    
  3. ----根据您的要求,您可能不得不使用PL / SQL。我会看看是否有人可以发布一个纯SQL解决方案(在这种情况下肯定会是这样)。

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

答案 1 :(得分:7)

如果孩子有FK将他们链接到父母,那么你可以在父母上使用DELETE CASCADE。

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

删除供应商,它将减少该供应商的所有产品

答案 2 :(得分:3)

这是一个如何完成的完整示例。 但是,您需要在子表上使用闪回查询权限。

这是设置。

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

现在删除子项的子集(父项为1,3和4但不是5)。

delete from child_tab where child_val = 100;

然后从child_tab的当前COMMITTED状态获取parent_ids(即它们在删除之前),并删除那些未删除会话的内容。这为您提供了已删除的子集。 然后,您可以从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'仍然存在(因为它在子表中没有条目)并且'Red'仍然存在(因为它仍然在子表中有一个条目)

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

select * from parent_tab;

这是一个异国情调/异常操作,所以如果我这样做,我可能会有点谨慎,并在交易开始时以独占模式锁定子表和父表。此外,如果子表很大,它将不会特别高效,所以我选择像Rajesh的PL / SQL解决方案。