【问题标题】:SQL Server foreign key filtered constraintSQL Server 外键过滤约束
【发布时间】:2019-05-14 21:11:30
【问题描述】:

我正在尝试在 SQL Server 数据库中进行软删除,我有以下简单的关系:

博客表:

PK BlogId, Name, Deleted

张贴表:

PK PostId, FK BlogId, Title, Deleted

Post 表有一个带有约束的外键,它确保每个帖子都有一个现有的博客,并且当您删除博客时,您应该删除所有相关的帖子。

但是,如何在考虑软删除的情况下建立 FK 约束,所以当我将博客记录设置为已删除时,它会强制我将所有相关帖子也标记为已删除。

更新:我发现临时表非常有用的功能可以替代软删除,因为它跟踪包括记录删除在内的整个更改历史记录。(但仍然对以某种方式进行软删除感兴趣问题)

【问题讨论】:

  • 您不能为此使用 FK 约束。您必须在删除逻辑(希望是存储过程)中处理这个问题,或者创建触发器来处理它。

标签: sql-server foreign-keys constraints


【解决方案1】:

您可以在BlogIdDeleted 字段上创建复合外键,并将ON UPDATE 选项设置为CASCADE

CREATE TABLE Blog (
    BlogId INT NOT NULL,
    [Name] VARCHAR(20) NOT NULL,
    Deleted BIT NOT NULL,
    CONSTRAINT PK_Blog PRIMARY KEY (BlogId),
    CONSTRAINT AK_Blog UNIQUE (BlogId, Deleted)
);
GO

CREATE TABLE Post (
    PostId INT NOT NULL,
    BlogId INT NOT NULL,
    Title VARCHAR(20) NOT NULL,
    Deleted BIT NOT NULL,
    CONSTRAINT FK_PostBlog FOREIGN KEY (BlogId, Deleted)
        REFERENCES Blog (BlogId, Deleted)
        ON UPDATE CASCADE
);
GO

INSERT INTO Blog VALUES (1, 'Blog 1', 0);
INSERT INTO Post VALUES (1, 1, 'Post 1', 0);

UPDATE Blog SET Deleted = 1 WHERE BlogId = 1;

-- Notice that the `Deleted` field in `Post` is now '1' and not '0'.
SELECT * FROM Post;

【讨论】:

  • 但现在我无法删除帖子:UPDATE Post SET Deleted = 1 WHERE PostId = 1;
  • 一种方法是创建标记为已删除的虚拟博客,例如INSERT INTO Blog VALUES (-1, 'Dummy', 1)。然后,每当您想删除帖子时,只需将帖子重新链接到虚拟博客,例如UPDATE Post SET BlogId = -1, Deleted = 1 WHERE PostId = 1。根据您的要求,您可能需要添加一个额外的列来记录与帖子最初相关的博客的 ID。