以下结构用视图替换了您的表,并且看起来与您描述的相似,除了它是在幕后维护而不是显式外键。我不知道你想支持哪些操作Revisions
,目前我只支持INSERT
:
create table dbo._Drawings (
ProjectId int not null,
DrawingId int not null,
constraint PK_Drawings PRIMARY KEY (ProjectID,DrawingID)
)
go
create table dbo._Revisions (
ProjectID int not null,
DrawingID int not null,
RevisionNo int not null,
_PreviousRevision as CASE WHEN RevisionNo > 1 THEN RevisionNo - 1 END persisted,
_NextRevision int null,
constraint PK_Revisions PRIMARY KEY (ProjectID,DrawingID,RevisionNo),
constraint FK_Revisions_Drawings FOREIGN KEY (ProjectID,DrawingID)
references _Drawings (ProjectID,DrawingID),
constraint CK_RevisionNos CHECK (RevisionNo >= 1),
constraint UK_Revisions_Previous UNIQUE (ProjectID,DrawingID,_PreviousRevision),
constraint UK_Revisions_Next UNIQUE (ProjectID,DrawingID,_NextRevision),
constraint FK_Revisions_Previous FOREIGN KEY (ProjectID,DrawingID,_PreviousRevision)
references _Revisions (ProjectID,DrawingID,RevisionNo),
constraint FK_Revisions_Next FOREIGN KEY (ProjectID,DrawingID,_NextRevision)
references _Revisions (ProjectID,DrawingID,RevisionNo)
)
以上两个表是数据的“后备存储”。该_Revisions
表确保修订序列从 1 严格单调递增。每一行维护一个外键到其紧邻的先前和后续修订,除了第一个和最后一个,NULL
s 替代(但唯一约束确保每个都只存在一个每个ProjectID
,DrawingID
组合。
create view dbo.Drawings
with schemabinding
as
select
d.ProjectID,
d.DrawingID,
r.RevisionNo as LatestRevision
from
dbo._Drawings d
left join
dbo._Revisions r
on
d.ProjectId = r.ProjectID and
d.DrawingId = r.DrawingID and
r._NextRevision is null
上面的视图模仿了您要求的Drawings
表格,并将用于任何实际的数据访问。如果您想强制执行每张图纸必须至少有一个修订版的不变量,您可以切换left join
到 aninner join
并将其设为indexed view。您需要添加一个触发器来支持INSERT
s,其方式与下面的 for 大致相同Revisions
,然后填充两个表。
create view dbo.Revisions
with schemabinding
as
select
ProjectID,
DrawingID,
RevisionNo
from
dbo._Revisions
此视图创建的印象Revisions
与您的查询一样简单
create trigger T_Revisions_I
on dbo.Revisions
instead of insert
as
;with SplitData as (
select ProjectID,DrawingID,RevisionNo,RevisionNo-1 as Prev, Seq
from inserted cross join (select 1 union all select 2) t(Seq)
)
merge into dbo._Revisions r
using SplitData s
on
r.ProjectID = s.ProjectID and
r.DrawingID = s.DrawingID and
(
(s.Seq = 1 and r.RevisionNo = s.Prev) or
(s.Seq = 2 and r.RevisionNo = s.RevisionNo)
)
when matched and s.Seq = 1
then update set _NextRevision = s.RevisionNo
when not matched and s.Seq = 2
then insert (ProjectID,DrawingID,RevisionNo) values (s.ProjectID,s.DrawingID,s.RevisionNo)
;
最后,这个触发器负责以_Revisions
我上面创建的约束所需的方式维护结构。诀窍是我们使用一个MERGE
语句,以便在插入新行的同时,我们还更新前一行,使其_NextRevision
列不再为空并引用我们正在插入的行。
可以添加更多触发器以支持更高级的使用。