2014-04-30 59 views
1

是否可以使用触发器实现on update cascade?我写了下面的示例代码(也http://sqlfiddle.com/#!6/d7298/1):通过SQL Server中的更新触发器级联外键软删除标志

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约束抛出一个错误这是行不通的。我希望级联软删除,以便只要父记录被软删除,子记录就会被软删除。

IsDeletedCFK可以让我级联更新到Child,但在高并发环境下,我相信它应该是可能的损坏的数据库状态,以结束:

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

在T1: 父是软的另一个独立的查询

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

回答

1

从关系的角度来看,你的PK是无效的,因为它有一个子集,它是一个有效的PK本身(Id列)。它也将允许有两个具有相同ID的行,但一个删除,一个不是(我认为这不是你以后的)。如果你真的想这样做,我只需要在Id列上做一个PK,也许在Id, IsDeleted上是唯一的。你的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 
+0

谢谢。我将采取独特的索引方法。你能评论多少存储空间分配给uq?它是每行4 + 1 = 5个字节吗?我相信在非聚集索引中,索引键的大小与聚集索引的大小相结合。只是想知道需要多少额外空间来维护上述数据结构。 – Mark13426

+1

如果表被聚集在'Id'上,开销很小。 NCI将包含来自CI的列,但不是两次,这是正确的。 – dean