ChatGPT解决这个技术问题 Extra ChatGPT

SQL——多对多表主键

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

Database Design

当您创建一个多对多表时,您应该在两个外键列上创建一个复合主键,还是创建一个自动增量代理“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 上发布的一个类似问题:stackoverflow.com/questions/344068/…
(试图将此添加到我之前的评论中,但不能)根据插入的数量,您还可以定期重建索引以确保它快速返回结果。在 SQL Server 中,您还可以调整索引的 FILLFACTOR 以在必须移动数据之前为插入提供足够的空间。
这个问题的答案不取决于所使用的 DBMS 吗?我怀疑 MySQL 在这种情况下会以某种方式运行,SQL-Server 会以另一种方式运行等等。
警告:如果没有特定的数据库标签,这里所说的大部分内容都是可疑的。不同的引擎工作方式不同!

p
paxdiablo

通过简单的两列多对多映射,我认为拥有代理键没有真正的优势。 (col1,col2) 上的主键保证唯一(假设您引用的表中的 col1col2 值是唯一的),并且 (col2,col1) 上的单独索引将捕获相反顺序执行更快的情况。代理是浪费空间。

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

在我看来,你在问题中提到的那条评论不值得它使用的电子。听起来作者认为表是存储在数组中,而不是性能极高的平衡多路树结构。

首先,不需要存储或获取已排序的表,只需索引即可。并且索引不会按顺序存储,它会以一种有效的方式存储,以便能够快速检索。

此外,绝大多数数据库表的读取频率远高于写入频率。这使得您在选择端所做的任何事情都比在插入端做的任何事情都更相关。


最后一点不是一个很好的概括:“绝大多数数据库表的读取频率远高于写入频率”。我发现了许多需要经常写入的关联表示例,例如将客户链接到订单的表。
@buffer,我会支持该评论(从技术上讲,只有当我说“所有表格”,“绝大多数”是基于经验时,它才是一个概括)。让我们也考虑一下您的示例,订单创建一次(它可能会偶尔更新,但这不太可能更改键/索引信息,更多的是命中订单状态等内容。但是,您需要执行的这些更新和选择打印发票或生成管理报告将超过原始插入。
想想亚马逊 - 每小时创建数以千计的订单。
@buffer,是的,但同样,这些订单中的每一个几乎肯定会被多次查询以进行(例如)包装、计费、状态更新、业务分析等。创建的绝对数量不如创建和读取之间的比率重要。
我的观点是,如果每小时执行数千次,insert 将很重要。您不能仅仅因为 insertselect 的比率是 < 而忽略它。 1.在这种情况下,客户关心下订单需要多长时间。
C
Community

链接表不需要代理键。

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

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

编辑:我在这里回答了同样的问题:SQL: Do you need an auto-incremental primary key for Many-Many tables?


在 col2 上使用 dups 索引而不是在 (col2, col1) 上使用唯一索引可能没问题。双列索引的优点是它允许单独对 col2 或对 col1 和 col2 进行仅索引扫描(尽管另一个索引 on (col1, col2) 也处理“两种”情况)。缺点是额外的列需要额外的存储空间。这通常并不重要,因此建议远非糟糕。然而,如果 col1 和 col2 很大或大小非常不同,您可以通过选择在较短的列上设置第二个索引来节省一些空间而不会影响性能。
@gbn: (col2, col1) 上的第二个索引不需要是唯一的,对吧?
在已经是 PK 之后在 (col1, col2) 上放置唯一索引是完全多余的
@mmcrae:我们在哪里做呢?
@mmcrae:您的评论是“在 (col1, col2).. 上放置一个唯一索引”。索引中的列顺序很重要。 (col2, col1) 不是 (col1, col2)(col1, col2) 的 PK 可能不适用于所有查询并生成扫描,因此使用相反的方法可以提高性能,因为它允许寻找 col2 更好的位置。例如,带有 col2 的表具有删除时的 FK 验证。检查子表 smuts
J
Jronny

如果引用了表,则可能需要增量主键。多对多表中可能存在需要使用增量主键从另一个表中提取的详细信息。

例如

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

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


谢谢!我在寻找与您描述的几乎相同的场景时得到了答案。但是您通过添加“其他详细信息”而偏离了您的第一句话。如果我有一个多对多映射表,我需要从另一个表中引用它怎么办?意思是,多对多映射表没有存储任何其他信息......额外的 ID 列是否有意义?如果不是,如何引用映射表的一条记录呢?
这里有两个选项,您可以使用复合键作为引用表中的外键(这会为您的新表添加一个额外的列),或者您可以为映射表创建一个 id 列并将唯一约束设置为原始复合主键,而新的 id 列将成为主键。
B
Bernhard Hofmann

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


m
michael kosak

因此,如果唯一的工作是链接两个表,那么最好的 PK 将是双列 PK。

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

索引或 PK 是确保没有重复项的最佳方式。 PK 让 Microsoft Management Studio 等工具为您完成一些工作(创建视图)