Since you want to perform soft deletes, you can accomplish what you want by adding some additional helper columns and foreign keys:
create table Books (
Id uniqueidentifier primary key,
Title varchar(255) not null,
Author varchar(255) not null,
Deleted datetime null,
_DelXRef as CASE WHEN Deleted is null then 0 else 1 END persisted,
constraint UQ_Books_DelXRef UNIQUE (Id,_DelXRef)
)
create table Categories (
Id uniqueidentifier primary key,
Name varchar(255) not null,
Deleted datetime null,
_DelXRef as CASE WHEN Deleted is null then 0 else 1 END persisted,
constraint UQ_Categories_DelXRef UNIQUE (Id,_DelXRef)
)
create table BookCategories (
BookId uniqueidentifier not null,
CategoryId uniqueidentifier not null,
_DelXRef as 0 persisted,
constraint FK_BookCategories_Books foreign key (BookID) references Books(Id),
constraint FK_BookCategories_Books_DelXRef foreign key (BookID,_DelXRef) references Books(Id,_DelXRef),
constraint FK_BookCategories_Categories foreign key (CategoryId) references Categories(Id),
constraint FK_BookCategories_Categories_DelXRef foreign key (CategoryId,_DelXRef) references Categories(Id,_DelXRef)
)
Hopefully, you can see how the foreign keys ensure that the _DelXRef
columns in the referenced tables have to remain 0
at all times, and so it's not possible to set Deleted
to any non-NULL value whilst the row is being referenced from the BookCategories
table.
(At this point, the "original" foreign keys, FK_BookCategories_Books
and FK_BookCategories_Categories
appear to be redundant. I prefer to keep them in the model to document the real FK relationships. I'm also using my own convention of prefixing objects with _
where it's not intended that they be used to the users of the database - they exist simply to allow DRI to be enforced)