复合主键:是好还是坏

时间:2014-09-27 19:46:55

标签: sql database database-design relational-database

我一直在为在线商店系统设计数据库。通过阅读本网站上的一些帖子我遇到的问题的问题是,虽然我可以使用复合主键,但我会在下面解释,这真的很糟糕吗练习(根据我在这方面读到的有关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,我不想要这个。

这就是为什么这里先前提出的问题已经(大部分)得出结论,复合键是个坏主意。所以我想为自己澄清一下;如果它不好,那么最佳做法是什么呢?

3 个答案:

答案 0 :(得分:43)

没有结论复合主键是坏的。

最佳做法是使用一些列来唯一标识一行。但是在某些表中,单个列本身不足以唯一标识行。

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表还缺少自己的自动增量单列主键。多对多表是很好地使用复合主键的典型示例。

答案 1 :(得分:19)

这个问题非常接近于提出可能产生宗教战争的意见。作为一个高度偏向于在我的表中使用自动增加的整数主键(称为TablenameId,而不是Id)的人,有一种情况是可选的。

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

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

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

答案 2 :(得分:5)

最佳做法最多是有用的,但最坏的情况是致盲。违背最佳做法并不是一种罪过。只要确定你知道你正在做出什么样的权衡。

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

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