复合主键:是好还是坏

JAX*_*JAX 45 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--
Run Code Online (Sandbox Code Playgroud)

现在,如果我只删除主键OrderId,我会在这里丢失我的一对一关系Many OrderIds can be associated to many PaymentIds,我不想要这个.

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

Bil*_*win 50

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

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

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)
Run Code Online (Sandbox Code Playgroud)

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

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

  • 值得指出的是,自动增量保证了表行的唯一性,但不一定是每个主题实体的单一身份.操作错误可能导致同一人,课程,产品等重复输入. (7认同)
  • @WalterMitty,对,像Ruby on Rails这样的ORM框架开始时只有"置信软件"这个短语只关于PK设计是`id`,但这就像是说你不会支持带有多个参数的函数.在第一个版本之后的版本中,RoR支持复合主键.所有框架最终都得出了相同的结论.如果有人仍在使用不支持复合PK的ORM,则需要升级. (6认同)
  • 反对声明复合主键的一些观点似乎是由一些ORM工具的工作方式驱动的.皮埃尔可能会或可能不会处于这种情况. (5认同)
  • @nickdnk无论如何,你是对的,按顺序插入可能是一个好处。请参阅此博客以获取一些解释和巧妙的图形证明:https://www.percona.com/blog/2015/04/03/illustating-primary-key-models-in-innodb-and-their-impact-on-disk -用法/ (2认同)

Gor*_*off 21

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

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

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

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

  • 请你更清楚地说明一下"对于外键,你肯定希望一列唯一地定义一行." (5认同)

Jer*_*meE 8

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

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

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


Ema*_*uel 5

最佳做法充其量是有用的,但最坏的情况是盲目的。违背最佳实践并不是罪过。只要确定您知道要进行哪种折衷即可。

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

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

  • “每个包含的键的复杂性可能会成倍增加” –请阅读此书的任何人对自己有所帮助,并研究101个关系数据库。该声明和以下参数是没有意义的。RDBMS所使用的标准索引结构(如B(+)Trees)用于处理复合键,而且组合键肯定不是指数级的。还有许多其他索引结构... (3认同)