如何创建一个向后关系的复合外键?

时间:2013-10-25 11:45:10

标签: sql sql-server sql-server-2008

对标题感到抱歉,想不出更好的写作方式。

这是我的问题...... 我的数据库中有2个表[图纸]和[修订];

[Drawings] 1-----* [Revisions]
 ProjectId(pk)      ProjectId(pk)(fk)
 DrawingNo(pk)      DrawingNo(pk)(fk)
                    RevisionNo(pk)
 LatestRevision

在[ProjectId]和[DrawingNo]中参考[图纸]的[修订版]中有一个外键。 我需要实现一种强制方法,即图纸最新版本号等于修订表中相应的修订号:

... WHERE [Drawings].[LatestRevision] NOT IN (
        SELECT [RevisionNo] 
        FROM [Revisions] 
        WHERE [Drawings].[ProjectId] = [Revisions].[ProjectId]
        AND [Drawings].[DrawingNo] = [Revisions].[DrawingNo])

如何将这样的东西放入外键?

我需要这个才能在sql server 2008 express上工作。

提前感谢您的帮助!

架构:

TABLE Drawings
( ProjectId varchar,
  DrawingNo varchar,
  LatestRevisions varchar,
  ...other columns
  PRIMARY KEY(ProjectId, DrawingNo)
)

TABLE Revisions
( ProjectId varchar,
  DrawingNo varchar,
  RevisionNo varchar,
  ...other columns
  PRIMARY KEY(ProjectId, DrawingNo, RevisionNo)
  FOREIGN KEY(ProjectId, DrawingNo) REFERENCES (Drawings(ProjectId, DrawingNo))
)

绘图'A'可以有修订版'1',绘图'B'可以有不同的修订版'1', 修订号本身并不是唯一的

2 个答案:

答案 0 :(得分:0)

我将采用以下架构:

TABLE drawings
( projectid      integer,
  drawingno      integer,
  latestRevision integer,
  primary key (projectid, drawingno)
)

TABLE revisions
( revisionno     integer primary key,
  projectid      integer,
  drawingno      integer,
  foreign key (projectid, drawingno)
      references (drawings (projectid, drawingno))
)

在这种情况下,我会发出:

ALTER TABLE drawings
ADD FOREIGN KEY (latestRevision)
REFERENCES (revisions(revisionNo))

这意味着每个revisions.revisionNo都是唯一的,列drawings.latestRevision是引用revisions表的主键的外键,即revisionNo。< / p>

如果您的架构有任何更改,请告诉我。

此外,仅当外键引用另一个表的主键时才会强制执行该外键。如果revisions.revisionno不是主键或者在此列上禁用了主键约束,则ALTER TABLE .. ADD FOREIGN KEY语句必然会返回错误。

答案 1 :(得分:0)

以下结构用您的描述替换您的表和外观,除了它在幕后维护而不是显式外键。我不知道您希望在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)
)

以上两个表是&#34;支持商店&#34;对于数据。 _Revisions表确保修订序列从1开始严格单调递增。每行保留一个外键到其前一个和后一个修订版,除了第一个和最后一个,NULL替换它们(但是唯一约束确保每个ProjectIDDrawingID组合只存在一个。

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切换为inner join并将其设为indexed view。您需要添加一个触发器来支持INSERT,其方式与下面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列不再为空引用我们正在插入的行。

可以添加更多触发器以支持更高级的使用。