ChatGPT解决这个技术问题 Extra ChatGPT

复合主键:好还是坏?

虽然可以使用复合主键,但对于下面的情况,这真的是一种不好的做法吗? Stackoveflow 上的共识似乎在这个问题上是双向的。

为什么?

我想将订单的付款存储在单独的表中。原因是,一个订单可以有许多项目,这些项目以多对多关系的形式在单独的表中处理。现在,如果我的付款表不使用复合主键,我将丢失唯一的 PaymentID

[PaymentId] INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
[OrderId] INT NOT NULL PRIMARY KEY --Also a Foreign Key--

现在,如果我只是删除 OrderId 的主键,我将在这里失去我的一对一关系,所以 Many OrderIds can be associated to many PaymentIds,我不想要这个。

这似乎就是为什么其他关于 SO 的答案(大部分)得出的结论是复合键是一个坏主意。如果不好,那么最佳做法是什么?

如果我理解正确,在这种情况下,您只需在 OrderId 上添加一个单独的唯一约束,并将 PaymentId 作为主键。
我不明白这部分:“原因是,一个订单可以有很多项目,这些项目也以多对多关系的形式在单独的表中处理。”?如果您在 payments 表中有 order_id,那么您所要做的就是用 orders 表引用它,您将如何丢失唯一的 PaymentID
@Laurence:是的,但是在这种情况下,1 个订单可以进行多次付款,这很糟糕,对吗?
在我看来,为一个订单多次付款一点也不坏。
@Surya:请看我之前的评论

B
Bill Karwin

没有结论说复合主键不好。

最佳做法是让一些列或多列唯一地标识一行。但在某些表中,单列本身不足以唯一标识一行。

SQL(和关系模型)允许复合主键。在某些情况下,这是一个很好的做法。或者,另一种看待它的方式是,在所有情况下这都不是坏习惯。

有些人认为每个表都应该有一个整数列,它会自动生成唯一值,并且应该作为主键。有些人还声称该主键列应始终称为 id。但这些都是惯例,不一定是最佳实践。约定有一些好处,因为它简化了某些决定。但是约定也是有限制的。

您的订单可能需要多次付款,因为有些人购买了 on layaway,或者他们有多种付款来源(例如两张信用卡),或者两个不同的人想为订单的一部分付款(我经常去和朋友一起去餐厅,我们每个人都为自己的饭菜买单,所以工作人员在我们每张信用卡上处理一半的订单)。

我将设计您描述的系统如下:

Products  : product_id (PK)

Orders    : order_id (PK)

LineItems : product_id is (FK) to Products
            order_id is (FK) to Orders
            (product_id, order_id) is (PK)

Payments  : order_id (FK)
            payment_id - ordinal for each order_id
            (order_id, payment_id) is (PK)

这也与identifying relationship的概念有关。如果定义仅因为订单存在而存在付款,则将订单作为主键的一部分。

请注意,LineItems 表也缺少自己的自动增量单列主键。多对多表是一个很好地使用复合主键的经典示例。


“在所有情况下这都不是一个坏习惯”。 . .我可以同意。
一些反对声明复合主键的观点似乎是由一些 ORM 工具的工作方式驱动的。皮埃尔可能会也可能不会在这种情况下。
@WalterMitty,对,像 Ruby on Rails 之类的 ORM 框架一开始就使用关于 PK 设计的“有意见的软件”这一短语仅是 id,但这就像说您不会支持具有多个参数的函数。在第一个版本之后,RoR 支持复合主键。所有框架最终都会得出相同的结论。如果有人仍在使用不支持复合 PK 的 ORM,则需要升级。
还值得指出的是,自动增量保证了表行的唯一性,但不一定保证每个主题实体的单一身份。操作错误可能导致重复输入同一个人、课程、产品等。
@nickdnk无论如何,您说得对,按顺序插入可能是一个好处。有关一些解释和巧妙的图形证明,请参阅此博客:percona.com/blog/2015/04/03/…
c
cezar

这个问题危险地接近征求意见,这可能引发宗教战争。作为一个高度倾向于在我的表中使用自动递增整数主键(称为 TablenameId,而不是 Id)的人,在一种情况下它是可选的。

我认为其他答案解决了您想要主键的原因。

一个非常重要的原因是为了参考。在关系数据库中,理论上任何实体都可以通过外键关系被另一个实体引用。对于外键,您肯定希望一列唯一地定义一行。否则,您必须处理彼此对齐的不同表中的多个列。这是可能的,但很麻烦。

您所指的表不是“实体”表,而是“连接”表。它是一种用于处理多对多关系的关系数据库结构。因为它并不真正代表一个实体,所以它不应该有外键关系。因此,复合主键是合理的。在某些情况下,例如当您担心数据库大小时,甚至需要省略人工主键。


如果您能回答@philipxy 的评论,那将很有帮助。因为我目前正处于考虑权衡的设计阶段。
@AnishRamaswamy 我认为他的意思是,如果要将两个表链接在一起,请通过唯一标识符链接它们。另一个表的主键成为您表中的外键。他说他的偏好是如果该主键不是复合键,因为他可能不希望将多个列导入他的表中,相反,他可能只想要一个。
我认为对这个旧答案发表评论以添加重要评论会很有帮助:TableNameId 是令人讨厌的多余。
J
JeromeE

磁盘空间很便宜,因此以约定命名的 int identity(1,1) 上聚集的主键(如 pk + 表名)是一个好习惯。它将使查询、连接、索引和其他约束易于管理。

但是,有一个很好的理由不这样做(至少在 MS SQL Server 中):如果您想在底层存储系统中管理数据的物理排序。

聚集的主键决定了物理排序顺序。如果您在标识列上执行此操作,则物理排序顺序基本上是插入顺序。但是,这可能不是最好的,特别是如果您总是以相同的方式查询表。在非常大的表上,获得正确的物理排序顺序会使查询速度更快。例如,您可能希望在两列的组合上使用聚集索引。


E
Emanuel

最佳实践充其量是有帮助的,但最坏的情况是盲目的。违背最佳实践并不是一种罪过。只要确保你知道你在做什么样的权衡。

数据库引擎可能是非常复杂的事情。在不知道给定引擎进行了哪些特定优化的情况下,很难确定哪种构造会产生最佳性能(因为我假设我们在这里讨论的问题是性能)。复合键对于一种数据库中的大表可能有问题,但对另一种数据库没有任何明显影响。

我学到的一个有用的做法是始终努力让我的应用程序尽可能简单。使用复合键是否可以让您不必在插入之前执行查找或其他一些麻烦事?使用它们。但是,如果您注意到使用它们会使您的应用程序不再满足某些重要的性能要求,请考虑不使用它们的解决方案。


A
Alfonso Tienda

如果您的具有复合主键的表预计会有数百万行,那么控制复合键的索引可能会增长到 CRUD 操作性能非常下降的程度。在这种情况下,最好使用一个简单的整数 ID 主键,其索引将足够紧凑,并建立必要的 DBE 约束以保持唯一性。

资源:

https://www.toptal.com/database/database-design-bad-practices


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅