另一种方法(没有Null,并且在FOREIGN KEY
关系中没有循环)是具有第三个表来存储“喜欢的孩子”。在大多数DBMS中,您需要在上附加UNIQUE
约束TableB
。
@Aaron可以更快地确定上述命名约定非常繁琐,并且可能导致错误。如果您Id
的表中没有所有列,并且出现的许多表中的列(连接在一起)具有相同的名称,通常会更好(并使您保持理智)。因此,这是一个重命名:
Parent
ParentID INT NOT NULL PRIMARY KEY
Child
ChildID INT NOT NULL PRIMARY KEY
ParentID INT NOT NULL FOREIGN KEY REFERENCES Parent (ParentID)
UNIQUE (ParentID, ChildID)
FavoriteChild
ParentID INT NOT NULL PRIMARY KEY
ChildID INT NOT NULL
FOREIGN KEY (ParentID, ChildID)
REFERENCES Child (ParentID, ChildID)
在(正在使用的)SQL Server中,您还可以选择所IsFavorite
提到的位列。每个父母的唯一喜欢的孩子可以通过过滤的唯一索引来实现:
Parent
ParentID INT NOT NULL PRIMARY KEY
Child
ChildID INT NOT NULL PRIMARY KEY
ParentID INT NOT NULL FOREIGN KEY REFERENCES Parent (ParentID)
IsFavorite BIT NOT NULL
CREATE UNIQUE INDEX is_FavoriteChild
ON Child (ParentID)
WHERE IsFavorite = 1 ;
不建议使用选项1的主要原因(至少在SQL-Server中不建议这样做)是,外键引用中的循环路径模式存在一些问题。
阅读一个很老的文章:SQL By Design:循环参考
在两个表中插入或删除行时,您将遇到“鸡与蛋”问题。我应该首先插入哪个表-而不违反任何约束?
为了解决这个问题,您必须定义至少一列可为空。(好的,从技术上讲,您不是必须的,您可以拥有所有列,NOT NULL
但只有在DBMS中(例如Postgres和Oracle),它们才实现了可延迟的约束。有关类似问题,请参见@Erwin的回答:有关如何在SQLAlchemy中使用复杂的外键约束这可以在Postgres中完成)。不过,这种设置感觉就像在薄冰上滑冰。
在SO处也检查几乎相同的问题(但对于MySQL),在SQL中,两个表相互引用是否可以?我的答案几乎相同。MySQL没有局部索引,因此唯一可行的选择是可为空的FK和额外的表解决方案。