一个包含两个外键列的数据库表是否需要有第三列作为主键?

11

如果一个数据库表包含两个外键列,是否应该有第三列作为主键?

我猜测不需要,因为这两个外键在它们自己的表中已经是唯一的主键了。

我正在使用MySQL,并且以下三个表都使用了InnoDB引擎。

=======================    =======================
| galleries           |    | images              |
|---------------------|    |---------------------|
| PK | gallery_id     |    | PK | image_id       |
|    | name           |    |    | title          |
|    | description    |    |    | description    |
|    | max_images     |    |    | filename       |
|    | enabled        |    |    | enabled        |
=======================    =======================

========================
| galleries_images     |
|----------------------|
| FK | gallery_id      |
| FK | image_id        |
========================

我是否应该在galleries_images表中添加主键?


即使我也这么想,但我们需要更多的细节。通常,任何数据库都会在表的主键上创建一个唯一索引。如果您的表只有这两个外键而没有主键,那么我担心索引不会被创建(未经确认,只是猜测)。这取决于表的用途和使用方式。 - Guru
3
等等!仅当您的模型需要在画廊和图像之间存在多对多关系时,才需要映射表。如果将PK设为“Image_ID”,则不再是多对多关系...只需在您的“Image”表中放置“Gallery_ID”,然后完成此操作即可。我不知道“InnoDB”是什么,但如果它会创建这样的垃圾,则可能有两种情况:它无法区分多对多关系和一对多关系,或者您不能。从物理设计开始建立数据库的问题在于您没有考虑关系是什么。 - Stephanie Page
@Stephanie 那么,如果单个图像可以与多个图库关联,那么映射表就是合适的吗?这将是多对多关系?但是,在我的情况下,由于我希望图像仅出现在一个图库中,因此这是一对多关系,映射表是不必要的? - Mike Moore
1
回复:通过PHP管理订单:这是一个你可能真正需要考虑并发性的情况。我可以同时打开两个Web浏览器吗?如果可以,那么我可能会尝试同时从每个浏览器更新该订单号。你需要考虑如何完成它。你会让他们重新排列,然后一次性写入新的顺序吗?你会在每次更改时写回,并且只在最后提交吗?无论对于你的应用程序来说有意义的是什么,都要编写代码以处理可能的冲突和管理它。在数据库中更容易管理。它是为并发性而构建的。 - Stephanie Page
1
@Let,锁定行,然后让客户端尝试操作它们通常是一个不好的主意。在您的情况下,在此应用程序中,您可能没有很多其他会话尝试操作相同的列表,对吧?但是如果您现在养成这种习惯,更大更复杂的应用程序将无法容忍这样的过程。您正在允许这些锁无限期地保持。假设客户端开始重新排序,您获得了锁定,他去吃饭了。在您知道他已断开连接之前需要多长时间?同时,这些行被锁定。使用锁定的最佳方法是尽快进入和退出。或者锁定其他内容。 - Stephanie Page
显示剩余3条评论
6个回答

10

理论上,如果两个外键(FKs)的组合在表中是唯一的,或者如果两个FK加上某些其他列的组合是唯一的,那么该表具有复合主键,就没有必要引入另一个键作为代理主键。然而,人们常常会发现添加额外的键。部分原因取决于使用具有复合主键的表数据的其他用途。如果它描述了将自身与其他表中的行相关联的内容,那么引入简单PK可能是有意义的。

一些软件似乎需要简单PK,即使关系数据模型并不要求这样做。


6
所有表格都应该有一个主键。不过,没有必要创建一个新的代理列作为主键。以约翰的例子为例,使用其他表的2个主键字段和日期字段组成复合主键是完全可以接受的。从实用的角度来看,如果PK本身在另一个表中被引用或绑定到不处理复合主键的各种控件上,那么创建一个新的代理列可能比复合主键更容易处理。根据您问题的更新,我建议将主键设置为gallery_id、image_id的复合主键。我不认为添加新列有任何好处。

2
虽然我手头没有证据,但我想使用整数代理键比使用复合键更快速地进行连接。因此,我总是在所有表上创建int代理键。 - David
@David - 可能是的。又要加入平衡考虑的因素了! - Martin Smith
哦,复合键对我来说是一个新的东西。我还没有考虑过那个选项。 - Mike Moore

5
答案通常是“是的”。您所描述的表格类型是一个“关联表”,用于存储关联。由于这些记录本身很有趣,并且您可能希望稍后查找它们,因此它们应该具有有意义的标识。
例如,也许您有一个“players”表和一个“matchups”表,用于您的网球联赛。 “matchups”可能仅包含两个相互对战的球员的外键;它是两个球员之间的“关联”。
但是以后您可能需要记录与该关联特定相关的其他信息:比赛发生的时间,比分等。当然,一旦您想要在同样两个球员之间进行多场比赛时,您就需要区分每场比赛。因此,您将希望为每个“matchup”赋予其自己的主键标识。
========================
| galleries_images     |
|----------------------|
| FK | gallery_id      |
| FK | image_id        |  <----- Should I add a PK to this table?
========================

在您的具体示例中,这里可能需要一个主键。一旦您需要记录与关联有关的任何元数据,您将需要该主键。此外,如果可以将相同的图像添加到同一画廊多次,则需要主键来区分两个记录。


此外,在您的示例中,两个玩家之间的比赛可能会发生多次,因此仅将两个玩家ID作为键是行不通的。 - Eric Petroelje
尽管如此,使用其他表的2个主键字段和日期字段作为复合主键是完全可以接受的。 - Martin Smith

2
如果一个特定的图像只能与一个单独的画廊相关联,则您的画廊-图像表中的组合是唯一的,您可以使用该字段对作为主键。
如果画廊-图像组合可以重复或者
如果您的架构包括更多将成为那些画廊-图像子级的表,则建议您包含额外的字段作为主键。

2
地址锁定问题的解决方案:
例如,您可以创建一个表Order_image_lock(gallery ID(主键),start_time)。
创建3个方法/存储过程:GetLock、CheckLock、DropLock。
当您想重新排序端口集合时,请调用GetLock以插入(gallary_id,sysdate)。
如果成功,则可以继续。如果在PK上失败,则有其他人正在重新排序,请引发异常。
当您准备好重新排序时,请调用CheckLock以查看您的锁是否仍然存在(您将了解原因)。如果您拥有它,请更新重新排序的值;如果没有,请转到GetLock。
完成后,DropLock删除记录。
服务器进程可以扫描超过x分钟的锁定表。适用于断开连接或离开屏幕并午餐。
还应向该表添加user_id列,以便您可以报告谁拥有哪些锁定,另一个用户可能需要这些锁定。
这比实际锁定行要扩展得多。一些DBMS具有有限数量的锁定,这迫使它们执行“锁定升级”,其中多个行锁定被转换为页锁定,直到有太多页锁定并且转换为表锁定...如果您计划进行扩展,则需要检查您的RDBMs如何处理大量锁定。

1

您说外键是其自身表中的主键。这意味着它们在这些表中是唯一的。但这并不意味着它们在本表中是唯一的。

我通常发现最好在数据库表上创建一个主键。迟早你会发现需要它,那么为什么不从一开始就包括新的主键呢?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接