我正在删除位于多对多关系的一个网站上的表格中的一行。我还想删除该关系另一边的任何相关行。
例如,假设我有以下表格,我想从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
表。
答案 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),但足以使工作在基线上进行。