繁体   English   中英

合法的? 两个外键引用相同的主键

[英]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成为UsersNetworks之间的多对多表:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM