SQL-多对多表主键


125

阅读此问题的评论后,出现此问题:

数据库设计

创建多对多表时,应该在两个外键列上创建一个复合主键,还是创建一个自动增量替代的“ ID”主键,然后将索引放在两个FK列上(也许独特的约束)?在每种情况下插入新记录/重新索引对性能有何影响?

基本上,这是:

PartDevice
----------
PartID (PK/FK)
DeviceID (PK/FK)

与这个:

PartDevice
----------
ID (PK/auto-increment)
PartID (FK)
DeviceID (FK)

评论者说:

将两个ID设置为PK意味着表将按照该顺序在磁盘上进行物理排序。因此,如果我们插入(Part1 / Device1),(Part1 / Device2),(Part2 / Device3),则(Part 1 / Device3),数据库将必须将表分开,并在条目2和3之间插入最后一个。对于许多记录,这变得非常成问题,因为每次添加一条记录都要涉及数百,数千或数百万个记录的改组。相比之下,自动递增的PK允许将新记录附加到末尾。

我问的原因是因为我一直倾向于在没有代理自动增量列的情况下执行复合主键,但是我不确定代理键实际上是否性能更高。


下面是贴在一个SO问题silimar:stackoverflow.com/questions/344068/...
托尼

(试图将其添加到我之前的评论中,但不能这样做)。根据插入的数量,您还可以定期重建索引以确保其快速返回结果。在SQL Server中,您还可以调整索引的FILLFACTOR,以便在必须移动数据之前为插入提供足够的空间。
托尼2010年

1
答案是否取决于所使用的DBMS?我怀疑MySQL会表现的方式在这种情况下,SQL-服务器略有以另一种方式等
拉杜Murzea

警告:没有特定的数据库标签,此处所说的大部分内容都是可疑的。不同的引擎工作方式不同!
瑞克·詹姆斯

Answers:


85

通过一个简单的两列多对多映射,我看不到具有代理键的真正优势。(col1,col2)保证主键打开是唯一的(假设您的col1col2被引用表中的值是唯一的),而单独的索引打开(col2,col1)将捕获相反顺序执行速度更快的情况。替代品是浪费空间。

您将不需要在各个列上建立索引,因为该表只能用于将两个引用的表连接在一起。

在我看来,您在问题中提到的评论不值其使用的电子。听起来好像作者认为表存储在数组中,而不是极高性能的平衡多向树结构。

首先,永远不需要存储或获取排序后的,而只需索引即可。而且索引不会顺序存储,它将以有效的方式存储以便能够快速检索。

此外,绝大多数数据库表的读取次数多于写入的次数。这使得您在选择端所做的任何事情都比在插入端所做的更重要。


最后一点不是一个很好的概括:“大多数数据库表的读取远比写入的频繁”。我发现许多需要经常写入的关联表示例,例如将客户链接到订单的表。
用户

5
@buffer,我将保留该评论(从技术上讲,只有当我说“所有表”,“绝大多数”是基于经验时,这才是概括)。让我们考虑一下您的示例,一个订单创建一次(它可能会不定期更新,但不太可能更改键/索引信息,更多内容不会影响订单状态。但是,这些更新和您需要执行的选择打印发票或生成管理报告将超出原始的插入内容
。– paxdiablo

想想亚马逊-每小时创建数千个订单。
用户

9
@buffer,是的,但是同样,几乎肯定会多次询问每个订单,以进行打包,计费,状态更新,业务分析等工作。创建的绝对数量不如创建和读取之间的比率重要。
paxdiablo

1
我的意思是,insert是否每小时要完成数千次将很重要。您不能仅仅因为insertto 的比率select<1而忽略它。在这种情况下,客户关心下订单要花费多少时间。
用户

19

链接表不需要代理键。

您只需要一个(col1,col2)上的PK和(col2,col1)上的另一个唯一索引

除非您使用无法应对并指示您的数据库设计的ORM ...

编辑:我在这里回答了同样的问题:SQL:您需要多对多表的自动增量主键吗?


3
您可以在col2上使用dups索引,而不是在(col2,col1)上使用唯一索引。双列索引的优点在于,它允许仅对col2或对col1和col2进行仅索引扫描(尽管(col1,col2)上的另一个索引也处理“两种”情况)。缺点是额外的列需要额外的存储空间。这通常并不重要,因此建议远非糟糕。但是,如果col1和col2很大或大小不同,则可以通过选择将第二个索引放在较短的列上来节省一些空间,而又不损害性能。
乔纳森·莱夫勒

@gbn:(col2,col1)上的第二个索引不需要唯一,对吗?
用户

1
在已经成为PK之后在(col1,col2)上放置唯一索引是完全多余的
Don Cheadle 2014年

@mmcrae:我们在哪里做?
gbn 2014年

2
@mmcrae:您的评论是“在(col1,col2)上放置唯一索引。”。索引中的列顺序很重要。(col2, col1)不是(col1, col2)。的PK (col1, col2)可能并不适合所有查询并生成扫描,因此通过执行PK 的相反操作可以提高col2的查找范围,从而提高了性能。例如,当带有col2的表具有删除时,进行FK验证。子表煤尘进行检查
GBN

12

如果引用了表,则可能需要增量主键。多对多表中可能包含一些细节,需要使用增量主键将其从另一个表中拉出。

例如

PartDevice
----------
ID (PK/auto-increment)
PartID (FK)
DeviceID (FK)
Other Details

使用PartDevice.ID作为FK可以很容易地提取“其他详细信息”。因此,需要使用增量主键。


1
谢谢!在寻找与您描述的几乎相同的场景时,我得到了答案。但是您通过添加“其他详细信息”而偏离了第一句话。如果我有一个多对多映射表,需要从另一个表中引用该怎么办?意思是,多对多映射表没有存储任何其他信息...附加ID列仍然有意义吗?如果不是,如何改为引用映射表的一条记录?
misanthrop

这里有两个选项,您可以将复合键用作引用表中的外键(这会将新列添加到新表中),也可以为映射表创建id列,并为原始复合物设置唯一约束主键,而新的id列将成为主键。
Vočko

6

我可以回答您问题的最简单,最直接的方法就是说,如果要链接的两个表没有顺序的主键,将会对性能产生影响。如您所述/引用,如果链接表没有自己的顺序主键,则链接表的索引将变得碎片化,或者DBMS将更加努力地插入记录。这就是大多数人将顺序递增的主键放在链接表上的原因。


2

因此,似乎如果唯一的工作是链接两个表,则最佳PK将是双列PK。

但是,如果它有其他用途,则添加另一个NDX作为带有外键和第二个唯一索引的PK。

索引或PK是确保没有重复项的最佳方法。PK使诸如Microsoft Management Studio之类的工具可以为您完成一些工作(创建视图)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.