复合主键:是好还是坏

问题描述 投票:37回答:4

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

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

sql database database-design relational-database
4个回答
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表也缺少自己的自动增量单列主键。多对多表是很好地使用复合主键的典型示例。


17
投票

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

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

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

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


7
投票

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

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

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


5
投票

最佳实践最多是有用的,但最坏的情况是致盲。反对最佳做法不是罪。只要确定你知道你正在做出什么样的权衡。

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

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

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.