CREATE TRIGGER Prevent_Cast_Delete ON Cast
INSTEAD OF DELETE
AS
BEGIN
DELETE FROM Cast WHERE id IN (
--Identify only people who work in films with 0 or 1 cast members
SELECT d.id
FROM
--The list of people we try to delete (request from elsewhere)
Deleted d
LEFT OUTER JOIN
--This query counts the cast for every film
(SELECT CastFilmID, COUNT(*) as CastCount FROM Cast GROUP BY CastFilmId) c
ON
c.CastFilmID = d.CastFilmID
--This where clause reduces the list of actor IDs we try to delete, to actually
--just those actors who are in a film of 1 cast member
WHERE c.CountCast < 2 OR d.CastFilmID IS NULL
);
END;
您忘记在触发器中实际执行删除操作,因此不会从您的表中删除任何内容。
我颠倒了逻辑:对于您尝试删除的所有记录,只有与没有其他演员共享电影的演员相关的记录才能被删除
为此,我们获取应用程序(例如)尝试删除的人员列表 - deleted d 伪表,将其加入演员表的摘要中,并仅选择摘要计数
Cast.ID, Cast.CastFilmID
KEANU, MATRIX
LAURE, MATRIX
HUGOW, MATRIX
JARAN, MOBYDI
JESUS, null
假设我说delete from cast where id in ('KEANU','JARAN','JESUS') - 这试图从我们知道的每一部电影中删除人。它将实现一个deleted的伪表:
Cast.ID, Cast.CastFilmID
KEANU, MATRIX
JARAN, MOBYDI
JESUS, null
电影数量:
Cast.ID, Cast.CastFilmID, CastCount
KEANU, MATRIX, 3
JARAN, MOBYDI, 1
JESUS, null, null
WHERE 子句从结果中删除了 KEANU,因为他在一部由 3 位演员(包括他自己)组成的电影中工作
原来请求的3条记录如下2条被删除(DELETE FROM cast WHERE ID IN (...this list...):
Cast.ID
JARAN
JESUS
请注意,已对您的数据库做出以下假设:
- Cast 表有一个 ID 列,每个演员电影都是唯一的
- CastFilmId 是电影表中的 ID,它对所有参与同一部电影的人重复
- 如果某部电影的演员没有出演该电影(有可能吗?)我假设他的 CastFilmID 为空。如果这永远不会为空,则可以对查询进行一些简化,但在这种形式中,我打算将演员未分配给电影的记录仍然可以删除
这里要欣赏的关键是运行删除查询的人会在触发器中为您提供 X 行;这些是该人试图删除的行,但您希望在您之前从该数据集之外获得额外知识(例如“有多少其他人与这些人中的每一个人在同一部电影中工作”)可以决定是否删除它们。这就是为什么我们计算那部电影的人数,将其加入被删除人的数据,然后决定是否应该删除该人
真的,这种魔法应该在前端完成。当您告诉数据库删除某些内容而它没有删除时,事情会变得非常混乱。同样,您必须注意 DELETE 查询一次可以删除数百行,因此您不能采用“删除只会尝试删除一个强制转换行”的简单方法-您必须将这些事情处理为数据集,将它们连接到其他数据集,并提出要删除的数据集