[英]Legit? Two foreign keys referencing the same primary key
就像Cliffs的更新一样,感谢ChaosPandion的模板。
人
PersonID Int PK
网络
PersonID Int PK FK
OtherPersonID Int PK FK
要么
人
PersonID Int PK
网络
PersonID Int PK FK
FriendID Int PK FK
朋友
FriendID Int PK
OtherPersonID Int FK
++++++++++++++++++++++++++
大家好,
我是一名网络开发人员,最近与一家公司合作开展了一个项目。 目前,我正在与他们的DBA合作获取网站的架构,我们对几个表的设计产生了分歧,我想就此事提出一些意见。
基本上,我们正在开发一个实现“朋友”网络的网站。 该站点的所有用户都将包含在具有(PersonID int identity PK等)的表tblUsers中。
我想要做的是创建第二个表,tblNetwork,它将保存用户之间的所有关系,具有(NetworkID int identity PK,Owners_PersonID int FK,Friends_PersonID int FK等)。 或者相反,删除NetworkID,并将Owners_PersonID和Friends_PersonID共享为主键。
这就是DBA遇到问题的地方。 他说“他只会在数据仓库架构中实现这种架构,而不是在网站上实现这种架构,这只是网络开发人员试图采取简单方法的另一个例子。”
显然,他的评论有点煽动性,这有助于激励我找到合适的答案,但更重要的是,我只想知道如何做对。 我已经开发了数十年的数据库和编程,与一些顶尖的人一起工作,从未听过这种论点。
DBA想要做的不是将Owners_PersonId和Friends_PersonId存储在同一个表中,而是创建第三个表tblFriends来存储Friends_PersonId,并让tblNetwork有(NetworkID int identity PK,Owner_PersonID int FK,FriendsID int FK(来自TBLFriends))。 所有那些tblFriends将会是(FriendsID int identity PK,Friends_PersonID(与人相关))。
对我来说,创建第三个表本质上是过分的,除了为Friends_PersonID创建别名之外什么都没做,并且导致我必须添加(我认为不需要的)加入我的所有查询,更不用说额外的周期在每个查询上执行连接所必需的。
我从技术上理解,他想要的是可能的,但它是否符合最佳实践? 什么是最佳做法?
感谢阅读,感谢评论。
瑞安
如果我理解你,你提议:
Person PersonID PK
FriendList FriendListID, OwnerID, PersonID
DBA建议:
Person PersonID PK
FriendList FriendListID, OwnerID
FriendListEntry FriendListID, PersonID
您的方法将需要列表中每个朋友的多行。 这将多次重复OwnerID,违反正常形式。 DBA的解决方案更加规范化,只有依赖于FriendList表中的FriendListID的值。
这里最好的做法是成为DBA的好朋友。 我会选择他的解决方案,因为它并不重要,你以后肯定需要他。
对我来说唯一有意义的架构是这样的:
Person
PersonID Int PK
Friend
PersonID Int PK FK
OtherPersonID Int PK FK
所以你可能有一个名为FriendList
的过程来执行这个漂亮的干净查询:
Select Person.*
From Friend
Inner Join Person On Friend.OtherPersonID = Person.PersonID
Where Friend.PersonID = @PersonID;
我不宽恕选择所有列。
如果Network.Owners_PersonID
以冗余方式存储在网络中,则您的设计会违反第三范式 。
但我不明白DBA的设计实际上有何帮助。 我希望Friends
成为Users
和Networks
之间的多对多表:
CREATE TABLE tblUsers (
PersonID INT IDENTITY PRIMARY KEY
);
CREATE TABLE tblNetworks (
NetworkID INT IDENTITY PRIMARY KEY,
Owner_PersonID INT NOT NULL REFERENCES tblUsers
);
CREATE TABLE tblFriends (
NetworkID INT NOT NULL REFERENCES tblNetworks,
FriendID INT NOT NULL REFERENCES tblUsers,
PRIMARY KEY(NetworkID, FriendID)
);
换句话说,你有一个简单的多对多关系:
Users ----<- Friends ->---- Networks
此外, Networks
引用Users
只是为了识别给定网络的所有者。 这样,给定网络只有一行,因此您无法通过更改某些行上的网络所有者来创建更新异常。
我不认为这会将实体过分地分成单独的表。 您仍然可以获得给定网络的朋友列表:
SELECT ... FROM Networks n JOIN Friends f ON (n.NetworkID=f.NetworkID)
你可以得到所有用户的朋友从所有网络这种方式(通过给定用户的ID为?
参数):
SELECT ... FROM Friends u
JOIN Friends f ON (u.NetworkID=f.NetworkID)
WHERE u.UserID = ?
在您的原始设计中,它几乎是相同的:
SELECT ... FROM Networks u
JOIN Networks f ON (u.Owner_UserID=f.Owner_UserID)
WHERE u.FriendID = ?
但优点是你已经消除了可能的更新异常。
我想要做的是创建第二个表,tblNetwork,它将保存用户之间的所有关系,具有(NetworkID int identity PK,Owners_PersonID int FK,Friends_PersonID int FK等)。 或者相反,删除NetworkID,并将Owners_PersonID和Friends_PersonID共享为主键。
我没有看到任何问题。 我同意NetworkID
是多余的 - 两个FK是表的自然键,所以你应该只使用它们作为主键,除非你有一些性能原因需要通过代理来引用特定的关系ID(在这种情况下您似乎没有)。
我说按你自己的方式行事。 拥有第三个表使编程部分更加痛苦。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.