虽然可以使用复合主键,但对于下面的情况,这真的是一种不好的做法吗? 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
?
没有结论说复合主键不好。
最佳做法是让一些列或多列唯一地标识一行。但在某些表中,单列本身不足以唯一标识一行。
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 表也缺少自己的自动增量单列主键。多对多表是一个很好地使用复合主键的经典示例。
这个问题危险地接近征求意见,这可能引发宗教战争。作为一个高度倾向于在我的表中使用自动递增整数主键(称为 TablenameId
,而不是 Id
)的人,在一种情况下它是可选的。
我认为其他答案解决了您想要主键的原因。
一个非常重要的原因是为了参考。在关系数据库中,理论上任何实体都可以通过外键关系被另一个实体引用。对于外键,您肯定希望一列唯一地定义一行。否则,您必须处理彼此对齐的不同表中的多个列。这是可能的,但很麻烦。
您所指的表不是“实体”表,而是“连接”表。它是一种用于处理多对多关系的关系数据库结构。因为它并不真正代表一个实体,所以它不应该有外键关系。因此,复合主键是合理的。在某些情况下,例如当您担心数据库大小时,甚至需要省略人工主键。
TableNameId
是令人讨厌的多余。
磁盘空间很便宜,因此以约定命名的 int identity(1,1) 上聚集的主键(如 pk + 表名)是一个好习惯。它将使查询、连接、索引和其他约束易于管理。
但是,有一个很好的理由不这样做(至少在 MS SQL Server 中):如果您想在底层存储系统中管理数据的物理排序。
聚集的主键决定了物理排序顺序。如果您在标识列上执行此操作,则物理排序顺序基本上是插入顺序。但是,这可能不是最好的,特别是如果您总是以相同的方式查询表。在非常大的表上,获得正确的物理排序顺序会使查询速度更快。例如,您可能希望在两列的组合上使用聚集索引。
最佳实践充其量是有帮助的,但最坏的情况是盲目的。违背最佳实践并不是一种罪过。只要确保你知道你在做什么样的权衡。
数据库引擎可能是非常复杂的事情。在不知道给定引擎进行了哪些特定优化的情况下,很难确定哪种构造会产生最佳性能(因为我假设我们在这里讨论的问题是性能)。复合键对于一种数据库中的大表可能有问题,但对另一种数据库没有任何明显影响。
我学到的一个有用的做法是始终努力让我的应用程序尽可能简单。使用复合键是否可以让您不必在插入之前执行查找或其他一些麻烦事?使用它们。但是,如果您注意到使用它们会使您的应用程序不再满足某些重要的性能要求,请考虑不使用它们的解决方案。
如果您的具有复合主键的表预计会有数百万行,那么控制复合键的索引可能会增长到 CRUD 操作性能非常下降的程度。在这种情况下,最好使用一个简单的整数 ID 主键,其索引将足够紧凑,并建立必要的 DBE 约束以保持唯一性。
资源:
https://www.toptal.com/database/database-design-bad-practices
id
,但这就像说您不会支持具有多个参数的函数。在第一个版本之后,RoR 支持复合主键。所有框架最终都会得出相同的结论。如果有人仍在使用不支持复合 PK 的 ORM,则需要升级。