我应该使用复合主键吗?

在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 } 。 它似乎也是一个相当有限的function,特别是如果连接表中的数据一直在变化,一旦主键序列回绕到0,您就会发现主键冲突。

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

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

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

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

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

如果db模型将来发生更改,则应更改,因为function已更改。 如果我们今天知道这个function将如何改变,我们将对此进行建模。 如果时间到来,任何未来的变化都将得到处理,我们无法预测例如对现有数据的影响,因此一个额外的专栏并不能保证它不会有任何未来的变化……

我们应该针对今天的function进行设计,并使db模型保持最简单,这样将来很容易改变/发展。

我个人认为,由于以下几个原因,您应该避免使用复合主键:

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

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

还有其他原因,但这些只是少数几个。

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

希望有所帮助。

关于SO也提出了类似的问题 ,没有达成共识;)

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

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

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

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

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

有关AppDevelopers的数据库开发错误的更多信息 。

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

OO人对“身份”有这种热心的想法,并会告诉你唯一重要的是你能够“识别”程序中的“现实生活中的对象”,以及复合的“现实生活”键在尝试实现这一目标时,只会让您陷入困境。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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