复合主键: 好还是坏?

虽然可以使用复合主键,但对于下面的情况,这真的是一个坏的做法吗?关于 Stackovflow 在这个问题上似乎是双向的的一致意见。

为什么?


我想在一个单独的表中存储订单的付款。其原因是,订单可以包含许多项,这些项以多对多关系的形式在单独的表中处理。现在,如果我不为我的支付表使用复合主键,我将失去我唯一的 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 的其他答案(大多数情况下)得出结论,复合键是一个坏主意。如果是坏的,那么什么是最好的练习呢?

73723 次浏览

没有结论表明复合主键是不好的。

最佳实践是使用 一些列或唯一标识行的列。但是在某些表中,单个列本身不足以唯一地标识一行。

SQL (和关系模型)允许复合主键。这是一个很好的做法是有些情况下。或者,从另一个角度来看,这并不是一个坏的做法,在所有的情况下。

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

你可能有一个多次支付的订单,因为一些人购买 分期付款,或者他们有多个支付来源(例如两张信用卡) ,或者两个不同的人想要支付一份订单(我经常和一个朋友去餐馆,我们每个人支付我们自己的饭菜,所以工作人员处理一半的订单对我们的信用卡)。

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

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)

这也与 认同关系的概念有关。如果支付仅仅因为订单的存在而存在是定义的,那么将订单作为主键的一部分。

注意,LineItems 表也没有自己的自动递增的单列主键。多对多表是很好地使用复合主键的经典示例。

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

我认为其他的答案解释了你为什么需要主键。

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

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

最佳实践往好里说是有用的,往坏里说则是盲目的。违背最佳实践不是罪过。只是要确保你知道你在做什么样的交易。

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

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

磁盘空间很便宜,所以集群在 int 标识(1,1)上的主键按照约定(如 pk + 表名)命名是一个很好的实践。它将使查询、连接、索引和其他约束更容易管理。

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

聚集的主键确定物理排序顺序。如果对标识列进行排序,则物理排序顺序基本上是插入顺序。但是,这可能不是最好的方法,特别是如果您总是以相同的方式查询表。 在非常大的表上,获得正确的物理排序顺序可以使查询快得多。例如,您可能希望将聚集索引放在由两列组成的组合上。

如果具有复合主键的表预计有数百万行,那么控制复合键的索引可能会增长到一定程度,从而导致 CRUD 操作性能严重下降。在这种情况下,最好使用一个索引足够紧凑的简单整数 ID 主键,并建立必要的 DBE 约束来维护惟一性。

来源:

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

从内部数据库引擎的角度来看,使用复杂的主键没有任何障碍。索引可以正常工作,完整性约束可以正常工作,没有性能缺陷等等。

理论中它们很好,但在 练习中有一些事情(可能的问题)需要考虑:

  1. 相关表中的外键(FK)必须有多列作为 PK。如果有一个简单的 PK,关系一致性将更容易维护。

  2. 在(web)应用程序中,客户多次需要选择/拾取一些数据(从 DB 表中检索)。每个记录的后面总是 PK,这导致了一些行动。如果这些记录表示为网络链接-这更容易维护为链接与多个 URL 参数。但是如果数据应该从下拉列表中选择,那么为 PK 实现更多参数的传输将是一个挑战,因为 HTML 表单下拉列表通常只使用一个关键参数。

一般来说,当你处理复杂的 PK (多列)时,你的代码应该为这些列(getters/setter)、应用程序中的参数传输、入侵检查等等进行乘法。最后,在应用程序开发期间,您可能会得出结论,尽管在 DB 建模期间这不是一个问题,但是拥有简单的 PK 可能更容易。