我必须创建一个数据库,并且必须确保我们可以在特定日期加载数据,所以我决定使用元组版本控制.
假设我们有以下两个表:
CREATE TABLE Author
(
Id UNIQUEIDENTIFIER NOT NULL,
Firstname VARCHAR(100) NOT NULL,
Surname VARCHAR(200) NOT NULL,
ValidFrom DATETIME NOT NULL,
ValidUntil DATETIME NULL,
PRIMARY KEY (ID, ValidFrom)
)
CREATE TABLE Book
(
Id UNIQUEIDENTIFIER NOT NULL,
Title VARCHAR(100) NOT NULL,
ISBN VARCHAR(100) NOT NULL,
AuthorId UNIQUEIDENTIFIER NOT NULL,
ValidFrom DATETIME NOT NULL,
ValidUntil DATETIME NULL,
PRIMARY KEY (Id, ValidFrom)
)
Run Code Online (Sandbox Code Playgroud)
当我第一次输入新作者时,我将生成一个新的GUID.我也在书表中使用此GUID来引用作者.
如果作者有更新,我创建一个具有相同GUID的新记录,但将当前日期定义为"ValidFrom",并将"ValidUntil"从原始记录设置为当前日期.
我不必更改book表,因为Author.Id没有改变.
我现在面临的问题是我想在Book.AuthorId = Author.Id上添加一个外键约束.
不幸的是,这不起作用,因为我使用复合主键.我不想将Author.ValidFrom添加到我的Book表中,因为我只想引用最新的而不是特定的版本.
关于如何解决这个问题的任何想法?我想我可以添加一个触发器,确保如果已经记录了一本书就无法删除作者,但我没有允许级联删除的解决方案.
我很感激每一个提示或建议.
这适用于 2008 年(依赖于使用 MERGE 语句以原子方式更改 Book 引用的行)。它确实引入了新列,您可能希望将它们隐藏在视图后面:
CREATE TABLE Author
(
Id UNIQUEIDENTIFIER NOT NULL,
Firstname VARCHAR(100) NOT NULL,
Surname VARCHAR(200) NOT NULL,
ValidFrom DATETIME NOT NULL,
ValidUntil DATETIME NULL,
Active as CASE WHEN ValidUntil is null THEN CONVERT(datetime,'99991231',112) ELSE ValidUntil END Persisted
PRIMARY KEY (ID, ValidFrom),
UNIQUE (ID,Active)
)
go
CREATE TABLE Book
(
Id UNIQUEIDENTIFIER NOT NULL,
Title VARCHAR(100) NOT NULL,
ISBN VARCHAR(100) NOT NULL,
AuthorId UNIQUEIDENTIFIER NOT NULL,
ValidFrom DATETIME NOT NULL,
ValidUntil DATETIME NULL,
PRIMARY KEY (Id, ValidFrom),
FK_Link as CONVERT(datetime,'99991231',112) persisted,
Foreign key (AuthorID,FK_Link) references Author (Id,Active) on delete cascade
)
go
declare @AuthorId uniqueidentifier
set @AuthorId = NEWID()
insert into Author(Id,Firstname,Surname,ValidFrom)
select @AuthorId,'Boris','McBoris',CURRENT_TIMESTAMP
insert into Book(Id,Title,ISBN,AuthorId,ValidFrom)
select NEWID(),'How to use tuple versioning','12345678',@AuthorId,CURRENT_TIMESTAMP
;with newAuthorInfo as (
select @AuthorId as Id,'Steve' as Firstname,'McBoris' as Surname,t.Dupl
from (select 0 union all select 1) t(Dupl)
)
merge into Author a
using newAuthorInfo nai
on
a.Id = nai.Id and
a.ValidUntil is null and
nai.Dupl = 0
when matched then update set ValidUntil = CURRENT_TIMESTAMP
when not matched then insert (Id,Firstname,Surname,ValidFrom)
values (nai.Id,nai.Firstname,nai.Surname,CURRENT_TIMESTAMP);
;with newAuthorInfo as (
select @AuthorId as Id,'Steve' as Firstname,'Sampson' as Surname,t.Dupl
from (select 0 union all select 1) t(Dupl)
)
merge into Author a
using newAuthorInfo nai
on
a.Id = nai.Id and
a.ValidUntil is null and
nai.Dupl = 0
when matched then update set ValidUntil = CURRENT_TIMESTAMP
when not matched then insert (Id,Firstname,Surname,ValidFrom)
values (nai.Id,nai.Firstname,nai.Surname,CURRENT_TIMESTAMP);
go
select * from Author
select * from Book
delete from Author where ValidUntil is not null
select * from Author
select * from Book
delete from Author
select * from Author
select * from Book
Run Code Online (Sandbox Code Playgroud)
对于 2008 年之前的解决方案,我认为没有比触发器更好的了。您可以引入第二个 Author 表,该表只有 Id 列(唯一),您可以从 Book 中 FK 列,然后从该表到 Book 进行级联删除。然后,您只需要在作者上有一个删除触发器,这样,如果您要从特定作者 ID 的作者中删除最后一行,则可以从这个新表中删除该行