复合主键为什么还在?

时间:2011-03-23 13:35:35

标签: mysql sql-server database-design

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

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

  • 外键变得模糊
  • 更难迁移或db-redesigns
  • 随着业务的变化而变得不灵活。 (我的车没有注册表。)
  • 使用约束更好地实现了相同的完整性。

它回归到候选键的设计概念,我没有看到它的重点。

它是软盘日的习惯/神器(最小化空间/索引),还是我错过了什么?

// //编辑 刚发现好的SO帖子:Composite primary keys versus unique object ID field //

9 个答案:

答案 0 :(得分:57)

当主键是非代理的时,复合键是必需的,嗯,复合,即可分解为几个不相关的部分。

一些现实世界的例子:

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

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

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

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

但是,有一些特定于实现的东西:

  • 某些系统不允许FOREIGN KEY引用任何非PRIMARY KEY的内容。

  • 有些系统只会在PRIMARY KEY上对表格进行聚类,因此PRIMARY KEY合成会提高合成中加入的查询的效果。

答案 1 :(得分:40)

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

例如(纯粹的例子 - 所以我们不在这里谈论数据库设计),假设你有一个ORDER表和ORDER_ITEM。如果您使用ProductIdLineNumber更新:以及Pedro提及OrderId或更好OrderNumber)作为{{1中的复合主键然后在ORDER_ITEM的交叉表中,您可以在SHIPPING中拥有ProductId。这可以大大提高您的性能,例如,如果您已经用完该产品,并且需要找出需要在不需要加入的情况下发运的SHIPPING_ORDERITEM的所有产品。

另一方面,如果您使用代理键,则必须加入并最终得到一个非常低效的SQL执行计划,它必须在多个索引上执行书签查找

请参阅书签查询上的more,其中使用代理键成为主要问题。

答案 2 :(得分:39)

我个人更喜欢使用代理键。但是,在连接仅包含来自其他两个表的ID的表(以创建多对多关系)时,复合键是可行的方式,因此将它们取出会使事情变得更加困难。

有一种观念认为代理键总是很糟糕,如果你没有通过使用自然键进行记录的独特性,那么你的设计就会很糟糕。我强烈不同意这一点(如果你没有存储SSN或其他一些独特的价值,我会藐视你为个人桌子提出一个自然的关键。)但是很多人觉得有必要进行适当的规范化。

有时使用复合键可以减少加入另一个表的需要。有时却没有。因此,有时复合键可以提高性能以及可能会损害性能的时间。如果密钥相对稳定,那么在选择查询时性能会更快。但是,如果它可能会像公司名称那样发生变化,那么当公司A更改其名称并且您必须更新一百万条相关记录时,您可能会受到伤害。

数据库设计中没有一种尺寸适合所有人。有时间复合键是有用的,有时它们是可怕的。有时代理键是有用的,有些时候不是。

答案 3 :(得分:9)

自然主键很脆弱。

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

几年前,有些系统是在假设社会安全号码是自然PK的情况下构建的,并且当SSN变得非独特且可以为空时,必须重新设计以使用身份。

因为我们无法预测未来,所以我们不知道后来的某些变化是否会过时成为一个完全正确和完整的模型。

答案 4 :(得分:8)

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

答案 5 :(得分:8)

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

实施例。您有从供应商处购买的零件清单。您可以像这样创建供应商和零件表:

SUPPLIER
SupplierId
SupplierName

PART
PartId
PartName
SupplierId
哦,哦。零件表允许重复数据。由于您使用了自动生成的代理键,因此您不会强制执行供应商的零件只能输入一次这一事实。相反,您应该像这样创建PART表:

PART
SupplierId
SupplierPartId
PartName

在此示例中,您的部件来自特定供应商,并且您希望强制执行规则:“单个供应商只能在PARTS表中提供一次”。因此,复合键。复合键可防止意外重复输入零件。

您可以随时将业务规则从数据库中删除并将其留给您的应用程序,但是通过将规则保留在数据库中(通过组合密钥),您可以确保在任何地方都强制执行业务规则,尤其是在您应该执行的情况下决定允许多个应用程序访问数据。

答案 6 :(得分:4)

就像函数封装一组指令或数据库视图抽象基表连接一样,代理键也可以抽象出它们所在实体的含义。

例如,如果您有一个保存车辆数据的表格,则应用代理项VehicleId从数据的角度抽象出它对车辆的意义。当你参考VehicleId = 1时,你肯定会谈论某种类型的车辆,但我们知道它是2008款雪佛兰Impala还是1991款福特F-150?不可以。任何车辆#1的基础数据是否随时都会发生变化?是。

答案 7 :(得分:4)

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

哲学答案:主键是行的标识。如果有一些信息是行的标识的固有部分(例如文章所属的客户......在多客户维基中) - 该信息应该是主键的一部分。

示例:用于组织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
);

答案 8 :(得分:2)

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

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

当存在与实体相关的多个弱表时,可以在查询子数据时从连接集中消除表。在孙子桌的情况下,可以将祖父母加入孙子而不将桌子放在中间。