我应该使用复合主键吗?

时间:2009-06-08 07:42:22

标签: java sql language-agnostic database-design

在Java的JPA(通过EmbeddedId或IdClass注释)中似乎只有第二类支持复合数据库键。当我读到复合键时,无论语言如何,人们都会不断发现,因为它们是一件坏事。但我不明白为什么。这些天使用复合键仍然可以接受吗?如果没有,为什么不呢?

我找到一个同意我的人: http://weblogs.sqlteam.com/jeffs/archive/2007/08/23/composite_primary_keys.aspx

但另一个不这样做的人: http://weblogs.java.net/blog/bleonard/archive/2006/11/using_composite.html

仅仅是我,还是人们无法区分复合键的适用与否?当表不表示实体时,即当它表示连接表时,我看到复合主键很有用。

一个简单的例子:

Actor { Id, Name, Email } Movie { Id, Name, Year } Character { Id, Name } Role { Actor, Movie, Character }

这里的Actor,Movie和Character显然可以从Id列作为主键获益。

但是Role是一个多对多连接表。我认为创建一个id只是为了识别数据库中的一行是没有意义的。对我而言,主键显然是{ Actor, Movie, Character }。它似乎也是一个相当有限的功能,特别是如果连接表中的数据一直在变化,一旦主键序列回绕到0,就会发现主键冲突。

那么,回到最初的问题,使用复合主键仍然是可以接受的做法吗?如果没有,为什么不呢?

10 个答案:

答案 0 :(得分:14)

在我个人看来,由于以下几个原因,你应该避免使用复合主键:

  1. 未来的变化:当您设计数据库时,您有时会错过将来变得重要的事情。一个重要的例子是认为两个或多个字段的组合是唯一的(因此可以成为主键),而将来你想要允许NULL或其他非唯一值。拥有一个主键是一个很好的解决此类更改的可靠解决方案。

  2. 统一性:如果每个表都有唯一的数字ID,并且您还对其名称保持一定的标准(例如“ID”或“tablename_id”),则引用它的代码和SQL更清晰(在我的中)观点)。

  3. 还有其他原因,但这些只是少数。

    我要问的主要问题是,如果您有一组独特的字段,为什么不使用单独的主键?费用是多少?一个额外的整数索引?那还不错。

    希望有所帮助。

答案 1 :(得分:10)

我认为使用复合键没有问题。

对我而言,数据库本身就是一个组件,应该像处理代码一样对待它们:例如,我们想要清晰的代码,清楚地传达它的意图,做一件事并做得好,那不是'添加任何不必要的复杂程度等等。

与db相同,如果PK是复合的,这就是现实,所以模型应该保持清洁和清晰。复合PK比混合自动增量+约束更清晰。当你看到一个不做任何事情的ID列你需要问什么是真正的PK,是否还有其他隐藏的东西你应该知道,等等。明确的PK不会留下任何疑问。

数据库是您应用的基础,对我来说,我们需要最坚实的基础。在此基础上,我们将构建应用程序(网络与否)。所以我不明白为什么我们应该弯曲数据库模型以符合一个开发工具/框架/语言中的某些特定内容。数据是指导应用程序,而不是相反。如果ORM在未来发生变化并变得过时,并且出现了另一种模式的更好解决方案,该怎么办?我们不能使用db模型来适应这个或那个框架,模型应该保持不变,它不应该依赖于我们用来访问数据的工具......

如果db模型将来发生变化,它应该会因为功能发生变化而发生变化。如果我们今天知道这个功能将如何改变,我们将对此进行建模。任何未来的变化都将在时机成熟时处理,我们无法预测例如对现有数据的影响,因此一个额外的专栏并不能保证它不会有任何未来的变化......

我们应该设计今天的功能,并尽可能简化db模型,这样将来很容易改变/发展。

答案 2 :(得分:6)

已经有人问过{p> Similar questions,并且没有达成共识;)

如果您开发了一个Web应用程序,那么您会喜欢单列pk,因为它们会使您的URL更简单。

对于要包装的序列,您需要在一个表(32位)中使用20亿条记录,或者在64位pk中使用10 ^ 18。

顺便说一句,您的数据模型不允许使用未知演员的电影角色。

答案 3 :(得分:5)

我的一般意见是......不。不要使用复合主键。

如果您使用ORM,它们通常会使ORM复杂化(ORM有时会调用复合主键“遗留行为”),并且通常如果您使用多个键,则其中一个或多个将倾向于自然而非技术密钥,对我来说是更大的问题:恕我直言,你当然应该支持技术主键。

Database Development Mistakes Made by AppDevelopers中的更多内容。

答案 4 :(得分:5)

在这个问题上,宗教战争已经并且仍在继续。

OO人对“身份”有这种热心的想法,并会告诉你,唯一重要的是能够在你的程序中“识别”“现实生活中的对象”的能力,以及“真实 - 生活“关键只会让你在试图实现这个目标时遇到麻烦。

数据人员有一个关于“唯一性”的东西,被OO方面视为“热心”,并且会告诉你唯一重要的是,如果业务告诉你(value for)属性的组合X和属性Y必须是唯一的,那么您的工作就是确保数据库强制执行此组合X + Y的唯一性业务规则。

您希望如何回答您的问题只是您喜欢哪种宗教信仰。我个人的宗教信仰是数据。自1969年以来,这种宗教已被证明能够在任何炒作和趋势中存活下来。

答案 5 :(得分:3)

这是一件宗教事情。我使用自然键和避免代理。无论是在理论上还是在实践中,我对复合键没有任何问题。

只有最简单的逻辑模型才会涉及没有复合键。叫我懒惰,但我认为没有必要通过在实现中将代理引入物理模型来使数据模型复杂化。当然,如果发现性能问题,我会在桌面上考虑一个,但我采用与非规范化相同的方法,即作为最后的手段。习惯使用代理人等于过早优化,IMO。

答案 6 :(得分:2)

在Ruby for Rails中,当没有明确指定时,你的Role表会像你描述的那样(如果列实际上是其他表中的ID)。但是,在数据库中,您可能希望通过在这三列上定义唯一索引来确保唯一组合,如果只是为了帮助数据库优化查询。使用该唯一索引并且框架不使用任何其他主键,您的Role表中不需要额外的数字主键。话虽如此,唯一索引可以被定义为复合主键。

至于未来的变化:为第一次迭代定义严格的数据库将防止意外数据被保留,这将使迁移变得更加容易。

所以:我会使用复合主键。

答案 7 :(得分:2)

我只会在连接表中使用它们。绝对确保每个记录标识符在一段时间内是唯一且一致的唯一方法是使用合成密钥。

复合键在理论上似乎没问题,这就是为什么它们很容易使用,但实践证明它们通常表明数据模型存在缺陷。更糟糕的是,在很多情况下,如果数据集足够大,它们将无法保证唯一性。数据集总是随着时间的推移而增长,因此使用它们可能意味着您已经在应用程序中植入了炸弹,只有在应用程序生产使用一段时间后才会爆炸。

我认为人们正在低估ORM。每种主流编程语言都有一个事实上的ORM,并且已经存在多年,因为它们解决了OO和关系结构之间的基本不兼容问题。试图在没有ORM的情况下针对SQL数据库编写任何复杂的,可测试的OO软件,效率非常低。

良好的ORM还提供了实践和工具,使创建和维护一致的高质量数据库模式变得更加容易,因此平均而言,团队将通过使用ORM来提前完成。手工制作模式就像编写C ++一样......人们可以做到这一点,但在现实世界中,随着时间的推移,保持质量很难以保证平均产​​品不好。

答案 8 :(得分:1)

就域模型而言,当表不表示实体时,即当它表示连接表(如您在问题中提到的那样)时,我认为创建复合主键没有任何问题,除非它如果不是单调增加,那么在插入期间您将获得一定数量的页面拆分。

有些ORM无法很好地处理复合主键,因此为主键创建代理自动整数可能更安全,并且使用非聚集索引覆盖列。

答案 9 :(得分:1)

我几乎从未见过复合键是个好主意的例子(例外,连接表只包含两个代理键)。在第一个地方,你在儿童餐桌上浪费空间。您正在损害连接中的性能,因为整数连接通常要快得多。如果您将组合键作为聚簇索引(在此处讨论SQL Server),则会导致数据库在存储记录方面效率降低,而在构建其他索引时效率较低 - 所有这些索引都使用聚簇索引。

当密钥中的数据发生变化时(因为它几乎不可避免地会发生),那么你需要更新所有相关的表,以及在数据库被设计为使用代理时完全不需要的任务上浪费大量不必要的更新和浪费处理能力键。主键不仅必须是唯一的,而且要保持不变。复合键通常无法通过第二次测试。

因此,您正在考虑使用一种损害性能的技术,导致内存和数据库存储使用不当,在子记录中使用更多空间(另一种资源浪费),并且需要痛苦地更新数百万个子记录时事情会改变的。哪些可能会使ORM难以使用?为什么要这么做?因为您懒得放置代理键,然后在潜在的复合键上定义唯一索引?使用复合索引是否有任何收益?由于缺乏5分钟的工作,你会永久伤害你的数据库吗?