Удаление связанных строк в отношениях «многие ко многим» - PullRequest
8 голосов
/ 14 октября 2011

Я удаляю строку в таблице, которая находится на одном сайте отношения «многие ко многим». Я также хотел бы удалить все связанные строки на другой стороне этого отношения.

Например, допустим, у меня есть следующие таблицы, и я хочу удалить строку из Cars. Я также хотел бы удалить любые связанные строки из Drivers и, конечно, любые строки, которые больше не нужны в CarDrivers.

Table Cars:
CarID      int
CarName    nvarchar(100)

Table Drivers:
DriverID   int
DriverName nvarchar(100)

Table CarDrivers:
CarID      int
Driver     int

Я знаю, как объединить таблицы выше в запросе SELECT. Но я не вижу, как удалить данные через отношения.

Примечание. Обе стороны отношения реализуют каскадное удаление. Так, например, удаление строки из Cars приведет к удалению любых связанных строк в CarDrivers. Но очевидно, что это не распространяется на таблицу Drivers.

Ответы [ 6 ]

5 голосов
/ 14 октября 2011

Я думаю, что лучше всего было бы сначала удалить данные из связанной таблицы. Другими словами, если вы хотите удалить Автомобиль и соответствующих Водителей, которые используют этот автомобиль, вам сначала нужно удалить Водителей, а затем Автомобиль. Таблица объединения удалит правильные записи из-за ON CASCADE DELETE.

Попробуйте это:

delete
from Drivers
where DriverID in
(
    select d.DriverID
    from Drivers d
    inner join CarDrivers cd
    on d.DriverID = cd.Driver
    inner join Cars c
    on c.CarID = cd.CarID
    where c.CarID = 1
)

delete
from Cars
where CarID = 1

Естественно, вам не нужно жестко кодировать 1, вы можете использовать все, что угодно, включая параметр, если вы используете этот фрагмент кода в хранимой процедуре.

1 голос
/ 19 октября 2013

Нет связи между таблицей Drivers и Cars.Это отношение через таблицу CarDrivers.Таким образом, проблема все еще существует.

Единственный известный мне способ автоматизации удаления CASCADE - это удалить FK между таблицей CarDrivers и Drivers и добавить триггер до или после удаления в CarDrivers, чтобы удалить запись в драйверах, где driver_id является одним изСтрока удаляется в CarDrivers.

Это не так во многих отношениях.Если удаление действительно требуется по всей таблице соединений, то отношение, вероятно, смоделировано неверно, и более чистые отношения должны были бы смоделировать отношения просто как «в машине есть много водителей» или FK of Cars.,Как отмечалось выше, для фактических отношений между автомобилями и водителями отношение «многие ко многим» действительно корректно, и вы никогда не удалите водителя только потому, что автомобиль был подсчитан / удален.

1 голос
/ 14 октября 2011

Ваш запрос не имеет смысла

Драйверы как сущности существуют отдельно от автомобилей. Автомобили могут водить многие водители, водители могут водить много автомобилей. Вот почему у вас есть таблица много-много.

Обратите внимание, что "водители могут водить много машин". Это означает, что если вы удаляете строку «Драйверы», вам необходимо удалить другие строки в CarDrivers.

Если вы все еще хотите это сделать, вам нужен триггер на CarDrivers. CASCADE от Drivers до CarDrivers удалит другие строки CarDrivers для вас. Не могу вспомнить поведение по умолчанию для рекурсии триггера.

Какой беспорядок.

Примечание: это почти имеет смысл, если у вас есть уникальность в одном из столбцов таблицы «многие-многие», тогда это должен быть внешний ключ между автомобилями и водителями («Уникальный на автомобиле» означает «максимум один водитель на автомобиль "означает пустую колонку FK в автомобилях)

0 голосов
/ 02 мая 2019

В Oracle вы можете справиться с этим с помощью триггеров, в частности, составных триггеров

alter table CarDrivers add CONSTRAINT CarFK FOREIGN KEY (CarID)
      REFERENCES Cars (CarID) on delete cascade enable
/    

create or replace TRIGGER "CarDrivers_delete"  
for delete ON CarDrivers 
compound trigger
  type driver_ids is table of Drivers.DriverID%type INDEX BY PLS_INTEGER;
  ids driver_ids;

  AFTER EACH ROW IS    
  BEGIN   
      ids (ids.COUNT + 1) := :NEW.Driver;    
  END AFTER EACH ROW;

   AFTER STATEMENT IS    
   BEGIN      
      FOR i IN 1 .. ids.COUNT    
      LOOP                                      
            delete from Drivers
             WHERE DriverID = ids (i);    
      END LOOP;    
   END AFTER STATEMENT;      
END;
/

Таким образом, достаточно выдать

delete from Cars where CarID = 666

, и удаление будет каскадно перенесено в таблицу CarDrivers с помощьюограничение и таблица драйверов с помощью триггера.

Использование составных триггеров необходимо, чтобы избежать ORA-04091, то есть изменения таблицы ошибок.Составные триггеры доступны начиная с Oracle11g. Смотрите здесь .

0 голосов
/ 14 октября 2011

Если у вас есть доступ к базе данных и у вас есть права на изменение таблиц, я бы просто создал внешние ключи и указал onupdate и oncascade следующим образом:

ALTER TABLE [dbo].[Drivers]  WITH CHECK ADD  CONSTRAINT [FK__Cars] FOREIGN KEY([CarID])
REFERENCES [dbo].[Car] ([CarID])
ON UPDATE CASCADE
ON DELETE CASCADE

ALTER TABLE [dbo].[CarDrivers]  WITH CHECK ADD  CONSTRAINT [FK_Drivers_Cars] FOREIGN KEY([CarID])
REFERENCES [dbo].[Car] ([CarID])
ON UPDATE CASCADE
ON DELETE CASCADE

Преимущество этого подхода заключается в том, чтоне нужно беспокоиться о бесхозных записях.В тот момент, когда вы удаляете одну запись из таблицы Car, все связанные в других таблицах автоматически удаляются и обновляются.Ваши операторы SQL тоже короче.

0 голосов
/ 14 октября 2011

Поместить каскадные удаления в таблицу CarDrivers.

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