我在表中有两个外键。我们假设该表名为News
,并且具有外键updatedById
和createdById
,两者都指向表userId
中的Users
。
现在我想在删除用户时设置为NULL
个外键,但当我尝试在这些关系中设置ON DELETE SET NULL
时,我得到:
在表'新闻'上引入FOREIGN KEY约束'FK_News_Users'可能 导致循环或多个级联路径。指定ON DELETE NO ACTION或 ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束。
我不明白为什么两个外键都不能设置为null?
答案 0 :(得分:8)
由单个触发的一系列级联参照动作 DELETE或UPDATE必须形成一个不包含循环的树 引用。 没有表格可以在所有列表中出现多次 由DELETE或UPDATE产生的级联引用操作。 此外,层叠引用动作的树必须没有更多 而不是指向任何指定表的一条路径。树的任何分支都已结束 当它遇到一个已经指定了NO ACTION的表时 是默认值。
在这种情况下,您可能需要考虑实现逻辑删除用户而不是物理删除用户的功能(例如,通过在Users
表中引入标记字段Active或Deleted)。这样,所有关系都保持不变,可以追溯分析。
但如果您仍然需要为两个FK实施ON DELETE SET NULL
,您可以在FOR DELETE
表格上使用User
触发器,如下所示:
CREATE TRIGGER Users_News_Delete_Trigger
ON Users FOR DELETE
AS BEGIN
UPDATE News SET createdById = NULL
WHERE createdById = DELETED.id;
UPDATE News SET updatedById = NULL
WHERE updatedById = DELETED.id;
END
答案 1 :(得分:3)
另一种方法是在表A和表B之间创建一个交叉引用表,其中每个条目都是A.ID和B.ID和B.ID有一个外键到B.然后你可以简单地将CASCADE删除到交叉引用。您需要在交叉引用中添加第三个字段,以说明引用的唯一目的,例如
[NewsID] INT NOT NULL DEFAULT 0,
[UsersID] INT NOT NULL DEFAULT 0,
[IsCreatedBy] bit NOT NULL DEFAULT 0
当然,您可以将这些字段从表A中取出。如果缺少这些字段,则左连接将为您提供null。
答案 2 :(得分:1)
我认为(在SQL Server中)不可能在同一个表上的2个或更多FK约束上执行此操作,指向相同的FK。
通常在这种情况下,您宁愿通过引入标记字段(例如,活动或已删除)在逻辑上删除用户。这样,所有关系都保持不变,可以追溯分析。 --- peterm
如果你想坚持设置NULL的最初想法,解决问题的方法是处理你在存储过程中删除用户并让它在之后立即执行更新。
CREATE PROCEDURE sp_DeleteUser
@UserId INT
AS
BEGIN
SET NOCOUNT ON;
DELETE FROM Users WHERE Id = @UserId;
UPDATE News SET created_byId = NULL WHERE created_byId = @UserId;
UPDATE News SET updated_byId = NULL WHERE created_byId = @UserId;
END
GO