通过SQL Server中的更新触发器级联外键软删除标志

时间:2014-04-30 04:41:26

标签: sql-server entity-framework triggers cascade soft-delete

是否可以使用触发器实现on update cascade?我编写了以下示例代码(也在http://sqlfiddle.com/#!6/d7298/1上):

create table Parent
(
    Id int not null,
    IsDeleted bit not null,
)

alter table Parent add constraint PPK primary key (Id, IsDeleted)

create table Child
(
    Id int not null,
    IsDeleted bit not null,

    ParentId int not null,
    ParentIsDeleted bit not null,
)

alter table Child add constraint CPK primary key (Id, IsDeleted)
alter table Child add constraint CFK foreign key (ParentId, ParentIsDeleted) references Parent(Id, IsDeleted)
go

create trigger ParentAfterUpdate on Parent
after update
as
begin
    if update(IsDeleted)
      update c set c.ParentIsDeleted = i.IsDeleted from Child c inner join Inserted i on c.ParentId = i.Id
end
go

insert into Parent values(100,0)

insert into Child values(1000,0,100,0)

update Parent set IsDeleted = 1

这不起作用,因为CFK约束会引发错误。我希望级联软删除,以便在父记录被软删除时软删除子记录。

IsDeleted中取出CFK可以让我将更新级联到Child,但在高度并发的环境中,我认为应该可能最终导致损坏数据库状态:

在T0:    实体框架将父级加载到内存中。父母不会被删除。

在T1:    父被其他独立查询软删除

在T2:    EF插入子记录,但由于IsDeleted不是外键的一部分,因此有一个活动子指向已删除的父记录。

1 个答案:

答案 0 :(得分:1)

从关系的角度来看,您的PK无效,因为它的一部分是有效的PK本身(Id列)。它也允许有两个具有相同Id的行,但是一个删除而一个不删除(我认为这不是你所追求的)。如果你真的想这样做,我会在Id列上制作PK,也许在Id, IsDeleted上制作一个唯一的PK。你的FK可以引用任何唯一的密钥,而不仅仅是PK。

此外,在声明FK时使用on update cascade选项,它将负责更新ParentIsDeleted列,然后您需要一个触发器传播到'IsDeleted'。见代码:

create table Parent
(
    Id int not null,
    IsDeleted bit not null,
)

alter table Parent add constraint PPK primary key (Id)
alter table Parent add constraint uq unique  (Id, IsDeleted)

create table Child
(
    Id int not null,
    IsDeleted bit not null,

    ParentId int not null,
    ParentIsDeleted bit not null,
)

alter table Child add constraint CPK primary key (Id, IsDeleted)
alter table Child add constraint CFK foreign key (ParentId, ParentIsDeleted) references Parent(Id, IsDeleted) on update cascade
go


create trigger trChildUpdate
on Child
after update
as
select trigger_nestlevel(object_id('trChildUpdate'), 'AFTER', 'DML');
if ((select trigger_nestlevel(object_id('trChildUpdate'), 'AFTER', 'DML')) > 1)
    return;
update c 
set c.IsDeleted = i.ParentIsDeleted 
from Child c inner join Inserted i on c.Id = i.Id
go


insert into Parent values(100,0)

insert into Child values(1000,0,100,0)

update Parent set IsDeleted = 1

select * from child