Удаление строк из родительских и дочерних таблиц - PullRequest
6 голосов
/ 04 марта 2011

Предположим, две таблицы в 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 ]

8 голосов
/ 04 марта 2011

Два возможных подхода.

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

---- Исходя из ваших требований, похоже, вам, возможно, придется использовать 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;
/
7 голосов
/ 04 марта 2011

Если у детей есть FK, связывающие их с родителем, вы можете использовать DELETE CASCADE на родителе.

например,

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

Удалить поставщика, и это приведет к удалению всех продуктов.для этого поставщика

3 голосов
/ 05 марта 2011

Вот полный пример того, как это можно сделать. Однако вам нужны привилегии запроса на флэшбэк для дочерней таблицы.

Вот настройки.

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;

Затем получите parent_ids из текущего состояния COMMITTED child_tab (т.е. как они были до ваших удалений) и удалите те, которые ваш сеанс НЕ удалил. Это дает вам подмножество, которые были удалены. Затем вы можете удалить их из 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);

«Зеленый» все еще там (поскольку у него все равно не было записи в дочерней таблице), а «Красный» все еще там (так как у него все еще есть запись в дочерней таблице)

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

select * from parent_tab;

Это экзотическая / необычная операция, поэтому, если бы я делал это, я, вероятно, был бы немного осторожен и заблокировал дочерние и родительские таблицы в эксклюзивном режиме в начале транзакции. Кроме того, если бы дочерняя таблица была большой, она не была бы особенно производительной, поэтому я бы выбрал решение PL / SQL, как у Раджеша.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...