Question

Is it possible to implement on update cascade using triggers? I wrote the following sample code (also on 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

This does not work as the CFK constraint throws an error. I was hoping to cascade soft deletes so that child records are soft deleted whenever a parent record is soft deleted.

Taking IsDeleted out of CFK can allow me to cascade the update down to Child, but in a highly concurrent environment, I believe it should be possible to end up with a corrupt database state:

at T0: Entity Framework loads parent into memory. Parent isn't deleted.

at T1: Parent is soft deleted by another independent query

at T2: EF inserts a child record, but since IsDeleted isn't part of the foreign key, there is an active child pointing to a deleted parent.

Was it helpful?

Solution

From the relational point of view, your PKs are not valid, because there is a subset of it which is a valid PK itself (the Id column). It will also allow to have two rows with the same Id, but one deleted and one not (which I presume is not what you're after). If you really want to go this way, I'd make a PK on just the Id column, and perhaps a unique on Id, IsDeleted. Your FK can reference any unique key, not just PK.

Furtheron, use on update cascade option when declaring a FK, it will take care of updating the ParentIsDeleted column, and then you'll need a trigger to propagate to 'IsDeleted'. See the code:

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top