删除多对多关系中的相关行

时间:2011-10-14 02:53:12

标签: sql sql-server sql-server-2008

我正在删除位于多对多关系的一个网站上的表格中的一行。我还想删除该关系另一边的任何相关行。

例如,假设我有以下表格,我想从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表。

7 个答案:

答案 0 :(得分:5)

我认为最好的办法是你必须先删除相关表格的数据。换句话说,如果您想要删除汽车以及使用该汽车的相应司机,您必须首先删除驱动程序,然后删除汽车。由于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 :(得分:1)

您的请求没有意义

作为实体的驱动程序与Cars分开存在。汽车可以由许多司机驾驶,司机可以驾驶许多汽车。这就是为什么你有很多表。

请注意“司机可以开多辆车”。这意味着如果删除Drivers行,则需要删除CarDrivers中的其他行。

如果想要这样做,则需要在CarDrivers上触发。从Drivers到CarDrivers的CASCADE将为您删除其他CarDrivers行。不记得触发器递归的默认行为。

多么糟糕。

注意:这个几乎是有意义的,如果你在多个表中的一个列上有唯一性,那么它应该是汽车和驱动程序之间的外键(汽车上的唯一意味着“最多”每辆车一个司机“意味着汽车中的NULLable FK列”

答案 2 :(得分:1)

Drivers和Cars表之间没有任何关系。这种关系是通过CarDrivers表。因此,问题仍然存在。

我知道自动执行CASCADE删除的唯一方法是删除CarDrivers和Drivers表之间的FK,并在CarDrivers之前或之后添加删除触发器以删除驱动程序中的条目,其中driver_id是要删除的行之一在CarDrivers。

这在很多方面都不干净。如果在整个连接表中实际需要删除,那么这种关系可能被建模错误,并且一个更清晰的关系就是将这种关系模型化为“有很多汽车司机”或者“驾驶员”表中的FK of Cars 。如上所述,对于实际的汽车和驾驶员关系,多对多关系实际上是正确的,您永远不会因为汽车被总计/删除而删除驾驶员。

答案 3 :(得分:0)

在CarDrivers表上放置级联删除。

答案 4 :(得分:0)

如果您有权访问数据库并有权更改表,我只需创建外键并指定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语句也更短。

答案 5 :(得分:0)

在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表,并通过触发器层叠到Drivers表。

必须使用复合触发器来避免ORA-04091,即突变表错误。从Oracle11g开始,可以使用复合触发器。 See here

答案 6 :(得分:0)

我的项目有一个类似的问题(将node.js与knex和postgres一起使用)。

但是我的多对多表外键都不能为空,这可能与您的示例有所不同,因为假定数据库中没有汽车就可以存在汽车。

通过对多对多应用不可空,限制和级联,我发现从每个表中分别删除(从迁移到它们的相反顺序)的删除效果很好。使用node / knex,我将这些函数导入到其他文件中,这些文件需要用作回调,并避免重复。

这可能不是最佳方法(sub-queries in knex don't read great),但足以使工作在基线上进行。