2011-05-20 69 views
9

我必须建立一个数据库,并确保它是在一个特定的日期,我们可以加载数据,所以我决定用tuple versioning元组版本和复合主键

比方说,我们有以下两个表:

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) 
) 

的第一次,当我进入一个新的作者,我会生成一个新的GUID。我在书籍表中使用这个GUID以及作者的参考。

如果作者有更新,我创建一个具有相同GUID的新记录,但将当前日期定义为“ValidFrom”,并将原始记录中的“ValidUntil”设置为当前日期。

我没有改变的书表,因为Author.Id没有改变。

我现在面临的问题是,我想在Book.AuthorId = Author.Id

添加一个外键约束,不幸的是,这并不工作,因为我使用的是复合主键。我不想将Author.ValidFrom添加到我的Book表中,因为我只想引用最近的一个而不是特定的版本。

关于如何解决这个问题的任何想法?我想我可以添加一个触发器,以确保在已经记录了一本书的情况下不能删除作者,但是我没有解决方案来允许级联删除。

我为每一个提示或提醒表示感谢。

+1

+1用于提及元组版本 – 2011-05-20 14:16:40

+1

您是否正在使用at最少的SQL Server 2008? – 2011-05-20 14:26:01

+2

梅根福克斯进行关系数据库设计+1。 – Yuck 2011-05-20 14:47:41

回答

3

这个工程于2008年(依赖于使用MERGE语句来改变其行要被书引用原子)。它不引入新的列,你可能想隐藏视图背后:

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 

对于2008年之前的解决方案,我不认为你可以做得比触发器更好。您可以引入第二个作者表,其中只包含Id列(唯一),您可以从Book中将FK反对,并从该表级联删除到Book。然后,您只需在作者上使用删除触发器,例如,如果您要删除作者的特定作者ID的最后一行,请从此新表中删除该行

+0

感谢您的建议,但我认为即使使用SQL Server 2008也不会运行,因为Author(ID,Active)上的UNIQUE CONSTRAINT仅在我们有一个或两个版本的记录时才有效。随着第三次更新,我们有两个无效的和一个有效的记录。 – MFox 2011-05-20 15:20:39

+0

@MFox - 我已经修复了它(并添加了第三个样本行),因此它在2008年有效。我将不得不考虑2005年的解决方案。 – 2011-05-20 17:07:47