两个外键引用一个表 - ON UPDATE SET NULL不起作用

时间:2013-01-27 22:42:10

标签: sql-server tsql foreign-keys constraints cascade

我在表中有两个外键。我们假设该表名为News,并且具有外键updatedByIdcreatedById,两者都指向表userId中的Users

现在我想在删除用户时设置为NULL个外键,但当我尝试在这些关系中设置ON DELETE SET NULL时,我得到:

  

在表'新闻'上引入FOREIGN KEY约束'FK_News_Users'可能   导致循环或多个级联路径。指定ON DELETE NO ACTION或   ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束。

我不明白为什么两个外键都不能设置为null?

3 个答案:

答案 0 :(得分:8)

Multiple Cascading Actions

  

由单个触发的一系列级联参照动作   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
相关问题