为什么复合主键还在?

我被分配到将一个数据库迁移到一个中级的ERP。 新系统在这里和那里使用复合主键,从实用的angular度来看,为什么?

与自动生成的ID相比,我只能看到消极的方面;

  • 外键变得模糊
  • 更难的迁移或数据库重新devise
  • 因业务变化而不灵活。 (我的车没有reg.plate ..)
  • 相同的完整性可以更好地实现。

它正在回落到键盘键的devise理念上,我没有看到这一点。

这是从软盘天(最小化空间/索引)的习惯/ artifact,还是我错过了什么?

//编辑//只find好的SO-post: 复合主键与唯一对象ID字段 //

我个人更喜欢使用代理键。 但是,在连接仅由两个其他表的ID组成的表(要创build多对多关系)时,组合键是一种方法,因此将其排除在外将会使事情变得更加困难。

有一种思想认为,代理键总是不好的,如果你没有独特的logging通过使用自然键你有一个糟糕的devise。 我强烈反对(如果你不存储SSN或其他独特的价值,我藐视你拿出一个人桌的天然钥匙),但很多人认为这是正确的正常化的必要条件。

有时使用组合键可以减lessjoin另一个表的需要。 有时候不会。 所以有时候一个复合键可以提升性能的同时也会影响性能。 如果密钥相对稳定,在select查询时可能会更快,性能更好。 但是,如果公司名称可能会发生变化,那么当公司A更改名称并且必须更新一百万条相关logging时,您可能处于一个受到伤害的世界。

数据库devise中没有一个适合所有人。 有时候复合键是有帮助的,而且时间太可怕了。 代用键有时候是有帮助的,有时候不是。

当你的主键是非代理的时候,复合键是必需的,而且固有地这个um是合成的,也就是说,可以分解成几个不相关的部分。

一些现实世界的例子:

  • 多对多链接表,其中主键由相关实体的键组成。

  • tenant_id是每个实体主键的一部分时,多租户应用程序和实体只能在同一个租户(受外键约束)内链接。

  • 处理第三方数据的应用程序(已经提供了主键)

请注意,在逻辑上,所有这些都可以使用UNIQUE约束来实现(除了代理PRIMARY KEY )。

但是,有一些实现特定的事情:

  • 有些系统不会让FOREIGN KEY引用任何不是PRIMARY KEY

  • 有些系统只会在PRIMARY KEY上聚簇一个表,因此使PRIMARY KEY组合可以提高组合查询的性能。

复合主键提供更好的性能,当他们被用作其他表中的外键,并减less表读 – 有时他们可以是救星。 如果您使用代理键,则必须转到该表以获取自然密钥信息。

例如(纯粹的例子 – 所以我们不在这里说DBdevise),可以说你有一个ORDER表和ORDER_ITEM 。 如果在ORDER_ITEM使用ProductIdLineNumber更新 :和Pedro提到OrderId或甚至更好的OrderNumber )作为复合主键,那么在您的交叉表中,您将能够在SHIPPING_ORDERITEM具有ProductId。 这可以大大提高你的性能,例如,如果你已经用完了那个产品,并且需要找出那个需要发货而不需要join的那个ProductId所有产品。

另一方面,如果使用代理键,则必须join,并且最终会出现非常低效的SQL执行计划,因为它必须在多个索引上进行书签查找

使用代理键查看更多关于书签查找成为主要问题。

自然主键很脆弱。

假设我们已经在(CountryCode,PhoneNumber)上build立了一个围绕自然PK的系统,并且在我们需要添加Extension的道路上还有几年,或者将PK更改为一列:Email。 如果这些PK列传播到所有的子表,这将变得非常昂贵。

几年前,有一些系统是假设社会安全号码是一个自然的PK,并且当SSN变得不唯一和可空的时候,必须重新devise以使用身份。

因为我们无法预测未来,所以我们不知道后来的一些改变是否会使过去曾经是完全正确和完整的模型变得过时。

非常简单的答案是数据完整性。 如果数据是有用的和准确的,那么可能需要键。 拥有“自动生成的ID”并不会删除其他密钥的要求。 另一种方法是不强制执行唯一性,并且接受数据将被复制,几乎不可避免地包含exception并导致错误。 你为什么要那个?

总之,组合键的目的是使用数据库来执行一个或多个业务规则。 换句话说:保护您的数据的完整性。

防爆。 你有从供应商处购买的零件清单。 你可以像这样创build你的供应商和零件表:

 SUPPLIER SupplierId SupplierName PART PartId PartName SupplierId 

呃哦。 零件表允许重复的数据。 由于您使用了自动生成的代理键,因此您不强制只能input一次来自供应商的部分。 相反,你应该像这样创buildPART表:

 PART SupplierId SupplierPartId PartName 

在本例中,您的零件来自特定的供应商,您希望执行规则:“单个供应商只能提供一个零件”。 因此,复合键。 您的组合键可防止意外重复input零件。

您可以始终将业务规则从数据库中留下,并将其保留到您的应用程序中,但是通过将规则保留在数据库中(通过组合键),确保业务规则在任何地方都被执行,特别是如果您决定允许多个应用程序来访问数据。

简短的回答:多列外键自然是指多列主键。 仍然可以有一个自动生成的ID列是主键的一部分。

哲学答案: 主键是行的标识 。 如果存在一些信息,这些信息是行的身份的固有部分(如多客户wiki中的文章属于哪个客户) – 信息应该是主键的一部分。

一个例子:组织局域网组织的系统

该系统支持几个LAN方面,参加人员和组织者相同:

 CREATE TABLE users ( users_id serial PRIMARY KEY, ... ); 

有几个派对:

 CREATE TABLE parties ( parties_id serial PRIMARY KEY, ... ); 

但是大多数其他的东西需要携带有关它链接到哪一方的信息:

 CREATE TABLE ticket_types ( ticket_types_id serial, parties_id integer REFERENCES parties, name text, .... PRIMARY KEY(ticket_types_id, parties_id) ); 

这是因为我们想要引用主键考勤表上的外键指向表格ticket_types

 CREATE TABLE attendances ( attendances_id serial, parties_id integer REFERENCES parties, ticket_types_id integer, PRIMARY KEY (attendances_id, parties_id), FOREIGN KEY (ticket_types_id, parties_id) REFERENCES parties ); 

正如函数封装了一组指令或数据库视图抽象基表连接一样,替代键抽象出它们所在实体的含义。

例如,如果您有一张载有车辆数据的表格,则应用代理VehicleId从数据的angular度抽象出车辆是什么意思。 当你参考VehicleId = 1时,你肯定是在谈论某种types的车辆,但是我们知道它是2008年的Chevy Impala还是1991年的福特F-150? 不pipe第一辆车的底层数据是否可以随时更改? 是。

虽然我更喜欢代理键,但在less数情况下我使用复合个案。 组合键可以完全或部分由代理键字段组成。

  • 多对多连接表。 无论如何,这些通常需要密钥对上的唯一密钥。 在某些情况下,密钥中可能包含其他列。
  • 弱的子表。 像命令行这样的东西不能独立存在。 在这种情况下,我使用组合表中的父(订单)表主键。

当有多个与实体相关的弱表时,在查询子数据时可能会从连接集中消除一个表。 在孙子桌子的情况下,可以在不涉及中间的桌子的情况下将祖父母join孙子。