Как обеспечить ссылочную целостность, основанную на ценностях - PullRequest
3 голосов
/ 31 мая 2019

Предположим, у меня есть таблица Level1:

Id int PK
Deleted bit

и еще один стол Level2:

Id int
Deleted bit
Level1id int

Я могу добавить FK к Level2, чтобы сопоставить Level1id с Level1.Id. Это прекрасно работает, если родитель существует.

Однако что делать, если я хочу убедиться, что записи могут быть INSERTed в Level2, когда соответствующая запись в Level1 имеет Deleted bit = 0?

Кроме того, как я могу предотвратить установку флага Deleted в Level1 = 1, если у любых дочерних записей в Level2 есть Deleted bit = 0? т.е. все дочерние записи должны иметь Deleted = 1, прежде чем родительская запись может иметь Deleted = 1

Я предполагаю, что мне, возможно, придется написать Trigger, но я надеюсь, что есть более элегантное решение ...

Ответы [ 2 ]

0 голосов
/ 31 мая 2019

Как уже упоминалось, вы могли бы реализовать эту логику в приложении или в хранимых процедурах, но чтобы сделать это в базе данных, следующие функции и ограничения CHECK должны дать вам то, что вы ищете.

Они будут запускаться / проверяться только для интересующей вас строки, когда вы делаете 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
0 голосов
/ 31 мая 2019

Вы можете сделать это с помощью ограничения внешнего ключа, но вам нужно добавить дополнительную информацию.

Сначала уникальное ограничение, включающее бит:

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

Затем добавьтеконстанта 0 значение в level2

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

Затем добавьте ограничение внешнего ключа:

alter table level2 add constraint fk_level2_id_deleted
    foreign key (level1id, deleted) references table1(id, deleted);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...