具有可为空列的复合外键

Dan*_*iel 7 sql-server referential-integrity foreign-keys

在下表中,是否有办法确保 PreviousID 始终引用具有匹配 ParentID 的行中的 ID,或者,如果 ParentID 为空,则它在引用的行中也为空?

CREATE TABLE MyTable (
  ID int not null identity(1,1) primary key,
  ParentID int null foreign key references MyTable (ID),
  PreviousID int null foreign key reference MyTable (ID),
    foreign key (ParentID, PreviousID) references MyTable (ParentID, ID)
)
Run Code Online (Sandbox Code Playgroud)

一个例子:

+-ID-+-ParentID-+-PreviousID-+  
| 1 | 空 | 空 |  
| 2 | 1 | 空 |  
| 3 | 空 | 2 | <-- 应该不可能,应该引用 ParentID 为 null 的 ID
+----+------------+------------+  

有没有办法强制执行?

更新:对于那些想知道的人,由于以下原因(从MSDN复制),复合外键不会强制执行此操作:

FOREIGN KEY 约束可以包含空值;但是,如果复合 FOREIGN KEY 约束的任何列包含空值,则会跳过对构成 FOREIGN KEY 约束的所有值的验证。要确保验证复合 FOREIGN KEY 约束的所有值,请在所有参与列上指定 NOT NULL。

更新:如果它有助于可视化所表示的数据结构,它是一个 B 树,其中具有相同父节点的节点组成一个链表。我试图强制链表中的每个“前一个”指针指向另一个具有相同父节点的节点。在父节点为空的情况下,它应该指向另一个父节点也为空的节点。

Jef*_*dge 1

尝试这个:

CREATE TABLE MyTable ( 
  ID int not null identity(1,1) primary key, 
  ParentID int null foreign key references MyTable (ID), 
  PreviousID int null foreign key references MyTable (ID), 
    foreign key (ParentID, PreviousID) references MyTable (ParentID, ID),
    unique (ParentID, ID),  /* Required for foreign key */
  check (PreviousID is null or ParentID is not null)  /* enforeces requested constraint */
) 
Run Code Online (Sandbox Code Playgroud)

结果:

insert into MyTable (ParentID, PreviousID) values (null, null) /* (1 row(s) affected) */
insert into MyTable (ParentID, PreviousID) values (1, null) /* (1 row(s) affected) */
insert into MyTable (ParentID, PreviousID) values (null, 2) /* The INSERT statement conflicted with the CHECK constraint. 
    The statement has been terminated. */
Run Code Online (Sandbox Code Playgroud)