如何确保依赖于值的参照完整性

时间:2019-05-31 10:10:35

标签: sql sql-server

假设我有一个表Level1

Id int PK
Deleted bit

和另一个表Level2

Id int
Deleted bit
Level1id int

我可以在FK上添加Level2,以使Level1idLevel1.Id匹配,这可以确保父项存在。

但是,如果我想确保当INSERTed中的匹配记录的Level2位= 0时,只能确保Level1中的Deleted记录,该怎么办?

此外,当Deleted中的任何子记录的Level1位= 0时,如何防止Level2中的Deleted标志被设置为1? 即所有子记录必须具有Deleted = 1,然后父记录才能具有Deleted = 1

我猜想我可能必须编写一个触发器,但是希望有一个更优雅的解决方案...

2 个答案:

答案 0 :(得分:0)

You can do this with a foreign key constraint, but you need to add some more information.

First a unique constraint that includes the bit:

alter table level1 add constraint unq_level1_id, deleted
    unique (delete, id);

Then, add a constant 0 value to level2

alter table level2 add (cast(0 as bit)) as deleted_0 persisted;

Then, add in the foreign key constraint:

alter table level2 add constraint fk_level2_id_deleted
    foreign key (level1id, deleted) references table1(id, deleted);

答案 1 :(得分:0)

As mentioned by others, you could implement this logic in an application, or in stored procedures, but to do it in the database, the following functions and CHECK constraints should give you what you're looking for.

These will only run/check for the row you're interested in when you do an INSERT/UPDATE.

CREATE FUNCTION Level1Deleted (@Id int, @Deleted bit)
RETURNS bit
AS

BEGIN

    IF @Deleted = 1

        RETURN 0

    ELSE

        RETURN
        (
            SELECT L1.Deleted
            FROM
                Level2 L2
                JOIN Level1 L1 ON L2.Level1Id = L1.Id
            WHERE L2.Id = @Id
        )

    ;

END
GO

ALTER TABLE Level2
WITH CHECK
ADD CONSTRAINT CHK_Level1Deleted
CHECK (Level1Deleted(Id, Deleted) = 0)
GO

CREATE FUNCTION Level2NotFullyDeleted (@Id int, @Deleted bit)
RETURNS bit
AS

BEGIN

    IF @Deleted = 0

        RETURN 0

    ELSE

        RETURN
        (
            SELECT CAST(COUNT(*) AS bit)
            FROM
                Level1 L1
                JOIN Level2 L2 ON L1.Id = L2.Level1id
            WHERE
                L1.Id = @Id
                AND L2.Deleted = 0
        )

    ;

END
GO

ALTER TABLE Level1
WITH CHECK
ADD CONSTRAINT CHK_Level2FullyDeleted
CHECK (Level2NotFullyDeleted(Id, Deleted) = 0)
GO