cub*_*729 24 normalization foreign-key database-design sql-server relational-theory
我想要一种一对多的关系,其中对于每个父母,一个或零个孩子被标记为“最喜欢的”。然而,并不是每个父母都会有孩子。(将家长视为本网站上的问题,将孩子视为答案,将最喜欢的视为已接受的答案。)例如,
TableA
Id INT PRIMARY KEY
TableB
Id INT PRIMARY KEY
Parent INT NOT NULL FOREIGN KEY REFERENCES TableA.Id
Run Code Online (Sandbox Code Playgroud)
在我看来,我可以将以下列添加到 TableA:
FavoriteChild INT NULL FOREIGN KEY REFERENCES TableB.Id
Run Code Online (Sandbox Code Playgroud)
或 TableB 的以下列:
IsFavorite BIT NOT NULL
Run Code Online (Sandbox Code Playgroud)
第一种方法的问题在于它引入了一个可为空的外键,据我所知,它不是规范化形式。第二种方法的问题是需要做更多的工作来确保最多只有一个孩子是最喜欢的。
我应该使用什么样的标准来确定使用哪种方法?或者,还有其他我没有考虑的方法吗?
我正在使用 SQL Server 2012。
ype*_*eᵀᴹ 21
另一种方法(没有空值和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)
Run Code Online (Sandbox Code Playgroud)
在 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 ;
Run Code Online (Sandbox Code Playgroud)
不推荐您的选项 1 的主要原因(至少在 SQL-Server 中不推荐)是外键引用中的循环路径模式存在一些问题。
阅读一篇很老的文章:SQL By Design: The Circular Reference
在两个表中插入或删除行时,您会遇到“先有鸡还是先有蛋”的问题。我应该先插入哪个表 - 不违反任何约束?
为了解决这个问题,您必须至少定义一列可以为空。(好吧,从技术上讲,您不必这样做,您可以拥有所有列,NOT NULL但仅在 DBMS 中,如 Postgres 和 Oracle,已经实现了可延迟约束。请参阅@Erwin 在类似问题中的回答:SQLAlchemy 中的复杂外键约束如何这可以在 Postgres 中完成)。尽管如此,这种设置感觉就像在薄冰上滑冰。
在SO(但对于MySQL)中检查一个几乎相同的问题在SQL中,两个表可以相互引用吗?我的答案几乎相同。MySQL 没有部分索引,所以唯一可行的选择是可空 FK 和额外的表解决方案。
Aar*_*and 11
这取决于您的优先事项是什么。你想逃避工作还是想遵守最严格的规范化规则?
就个人而言,我认为最好IsFavorite在子表中有,并且愿意投入工作以确保每个父母最多一个孩子是父母的最爱。主要原因与可空外键无关:我根本不喜欢指向两个方向的外键的想法。
@ypercube 的建议也是一个很好的妥协。
顺便说一句,拜托,拜托,请不要用无意义的列名乱扔你的架构,比如Id. 我更愿意看到在Id整个架构中以有意义的方式命名。它可以识别作者吗?好吧,叫它AuthorID。它代表一个产品吗?好的,ProductID。它是雇员,在某些情况下是指经理吗?好了,EmployeeID并且ManagerID更有意义,我比ID及Parent。虽然忽略它似乎是合乎逻辑的(并且将其放入是多余的),但是当您开始编写复杂的连接(或在此处发布查询)时,当您尝试对一堆连接进行逆向工程时,您肯定会感觉到一些诅咒到像a.Parent = b.ID... blecch 的列。