【问题标题】:Why single primary key is better than composite keys?为什么单主键比复合键好?
【发布时间】:2011-02-09 17:05:47
【问题描述】:

为什么拒绝复合键而支持使用名为 id 的单个主键的所有表?因为通常所有的 ORM 都遵循这个。

编辑

我刚开始在 Rails 上学习 ruby​​,在 pragmatic 的敏捷开发书中有一句话:--- Rails 真的不能很好地工作,除非每个表都有一个数字主键。对列的名称不那么挑剔。 我在学习 Doctrine 时读到的同一种台词。

EDIT2 请也检查此链接。我越来越对这件事感到困惑:--- Composite primary keys versus unique object ID field

从上面的链接:--

*主键应该是常量且无意义的;非代理键通常最终无法满足一项或两项要求

如果密钥不是恒定的,那么您未来的更新问题可能会变得非常复杂 如果密钥不是无意义的,那么它更有可能改变,即不是恒定的;见上文

举一个简单的常见示例:库存物品表。将商品编号(SKU 编号、条形码、零件代码或其他任何内容)作为主键可能很诱人,但一年后所有的商品编号都发生了变化,您将面临一个非常混乱的更新——整个——数据库问题...

编辑:还有一个比哲学更实际的问题。在许多情况下,您会以某种方式找到特定的行,然后更新它或再次找到它(或两者兼而有之)。使用复合键,在 WHERE 子句中有更多的数据需要跟踪,更多的约束用于重新查找或更新(或删除)。与此同时,关键部分之一也可能发生了变化!使用代理键,始终只保留一个值(代理 ID),根据定义,它不能更改,这大大简化了情况。*

【问题讨论】:

  • 你能引用你的来源吗?我同时使用:复合键和基于序列的 ID 主键。有时一个比另一个更合适。
  • 你在使用什么 ORM,它提倡总是使用简单键而不是复合键?我从来没有遇到过不能很好地使用复合键的 ORM。据我所知,没有充分的理由给予例如联结表自己的代理键。
  • @FrustratedWithFormsDesigner @Iain Galloway 我在学习 Doctrine 时读到了这篇文章,现在我在学习 Ruby on rails 时遇到了同样的事情。如果你想要写这行的书的确切页码,请告诉我。我是初学者,所以我要的是真实的东西。不要对此进行交叉提问或辩论。表示他们使用此 ID 字段的原因。 ??
  • 在这种情况下,我认为“主键优于复合键”的说法可能在 Ruby-on-Rails 和 Doctrine 的上下文中是正确的。这可能是因为底层架构(但我不是 Rails 或 Doctrine 方面的专家,所以我不能安全地进一步评论;)),但该声明应该 not 被假定为自动适用于其他环境/ORMS。
  • @FrustratedWithFormsDesigner 看看编辑部分。检查链接。

标签: database-design orm primary-key


【解决方案1】:

我不认为你应该只使用一个名为 id 的主键。

大多数人使用代理主键作为自动生成的 int,因为它将主键与需要更改的情况隔离开来,例如,如果您将 PK 设为用户名,而他们后来更改了他们的法定名称。您必须更新 PK 和所有 FK 列以反映新名称。如果您使用了代理主键,则只需在一个位置更新用户名(因为表连接的是 int 而不是名称)。

主键的大小很重要,因为 PK 会复制到您在表上构建的每个索引中。如果 PK 很大(如字符串),则索引中每页的键数较少,索引将占用更多缓存来存储它。整数很小。

具有自动增量 int PK 使其本身成为一个很好的聚集索引,因为行是按此顺序存储的,无需返回并将行移开以插入新行,您总是添加到桌子的尽头。

【讨论】:

  • 顺序 guid 也非常有效,尤其是当您需要将来自多个数据库的数据集合并到一个表中时。
  • @Juliet,但它们很大并且浪费索引空间。如果可能,最好将自动增量 int ID 与另一列结合起来,以标识该行来自的数据库/公司/等(如 1 或 2 字节 int),因为它可以使用比 guid 少得多的空间,但仍然在所有数据库中都是唯一的。
  • 我不建议使用 guid,除非是边缘情况
  • @KM 我刚开始在 Rails 上学习 ruby​​,在 pragmatic 的敏捷开发书中有一句话:--- Rails 真的不能很好地工作,除非每个表都有一个数字主键.对列的名称不那么挑剔。我在学习 Doctrine 时读到的同一种台词。
【解决方案2】:

我在使用复合键时遇到的唯一真正限制是使用带有子查询的IN 表达式。这是一个问题,因为 IN 表达式中的子查询必须返回单个列(至少在 T-SQL 中)。

SELECT
    emp.Name,
    emp.UserDomain,
    emp.UserID
FROM
    employee emp
WHERE
    ???? IN (SELECT e.UserDomain, e.UserID FROM ... /* some complex 
                                                       non-correlated subquery 
                                                       or CTE */
            )

当然,总会有变通办法,但有时可能会令人烦恼。

这几乎不是在有意义的地方避免使用复合键的理由。

【讨论】:

    【解决方案3】:

    你可以同时使用。在某些情况下,当在实体之间建立关联时,您可以将两个实体键用作复合键。

    根据经验,我对实体使用生成的 ID,对关系使用复合键。

    【讨论】:

    • 这是我使用的相同方法,而且效果很好。
    【解决方案4】:

    嗯,它基本上是关于保持 JOIN 简单 - 哪个更容易理解:

    SELECT
       p.ID, p.Name, p.City,
       c.ID, c.Country, c.ISOCode
    FROM
       dbo.Parent p
    INNER JOIN
       dbo.Child c on c.ParentID = p.ID
    

    SELECT
       p.ID, p.Name, p.City,
       c.ID, c.Country, c.ISOCode
    FROM
       dbo.Parent p
    INNER JOIN
       dbo.Child c ON c.ParentName = p.Name
         AND c.ParentCity = p.City
         AND c.ParentCountry = p.Country
    

    如果您有复合主键,任何从子表加入您的表的人都必须“拖动”所有这些列,并且所有这些列也将出现在子表中,并且 JOIN 语句非常混乱。为 JOIN 使用单个(甚至是代理)键会更好!

    【讨论】:

    • 您是否建议使用代理键(而不是复合键)作为表示多对多关系的联结表的 PK?如果有,为什么?
    • @Iain Galloway:不一定 - 多对多关系的联结表是您可能会使用复合 PK 的特殊情况之一。但是即使在这种情况下,添加代理 PK 也没有什么坏处(除了额外的列) - 它可以让您更轻松地管理(例如删除)该联结表中的条目
    • 此外,如果您的联结表变得不仅仅是一个联结,而是拥有它自己的属性和属性,那么拥有一个代理主键会有所帮助。如果您将其他表挂在联结表之外,则尤其如此……如果您为 PK 保留一个复合键,那么所有这些 OK 列都必须复制到这些附加表中。使用单个 PK,子表只需将单个 FK 列添加到联结表的单个 PK 列。随着时间的推移,单个 PK 列可以帮助更好地支持架构更改和数据库设计增长。
    【解决方案5】:

    我开发了一个具有 11 列主键的应用程序。每次我想保证我正在更新一行时,一遍又一遍地重新输入列表总是很有趣。它是错误的驱动程序,MS-Access 无法处理 PK 中超过 10 列等。

    大型复合键是设计异味,这意味着表包含异构实体,或者设计者不确定每个实体的独特之处。 (就像假设头发颜色、眼睛颜色和体重应该足以唯一地识别员工——这不是一个好的关键,因为您需要越来越多的列才能使其工作,最终将包括字段易挥发并且变化很大,例如体重,或者对于某些人来说头发颜色或缺乏头发颜色。)

    【讨论】:

      【解决方案6】:

      虽然我同意其他受访者给出的大部分理由,但我更喜欢单列整数键的主要原因是它使编写用户界面变得更加容易。

      如果您使用某种列表控件来表示您的数据(列表、列表视图、组合框等),您可以通过与项目一起存储的单个整数值将每个条目唯一地关联回其数据库表示。大多数预先编写的组件已经允许您为每个项目附加一个整数,对于那些不支持的组件,扩展组件非常容易。

      如果您在服务器应用程序和网页之间传递数据,则将单个标识值存储在代表数据的小部件的 id 属性中要比编写和解析多值 id 容易得多.

      【讨论】:

        【解决方案7】:
        1. 对于 ORM,只有一个标识 具有一致名称的列,例如 table_id 比组合更容易 钥匙。但是每个好的 ORM 支持 复合键。

        2. 一个简单的PK就可以轻松搞定 由数据库“自动递增”。 这不适用于复合材料 键。

        3. 简单的PK也更容易在 查询。当你需要加入时,你 只需使用两者中的一列 关系。

        这并不是说简单的 PK 比复合的更好。

        【讨论】:

          【解决方案8】:

          OO 编程中的对象无论其内容如何都具有标识性。关系数据库中的行(元组)仅由其内容标识。因此,当真正进行 ORM 时,即将对象从面向对象的编程语言映射到关系数据库时,必须提供一个额外的 ID,以区别于对象在程序中具有的字段和/或属性——除非其中一个或多个是以某种方式知道唯一地识别对象。

          【讨论】:

          • 您的表肯定需要一个主键,但没有理由必须是单列。复合主键非常常见,尤其是在联结表上。
          • 请再读一遍。我没有说在关系数据库模式中需要主键(它们不是),也不是在设计关系数据库模式时不会出现复合键(它们确实如此)。我只想说,如果您的关系数据库模型是从 OO 类模型中盲目生成,则所有表都需要具有代理 ID 作为它们的主键,它们不对应于对象类。除非您以特殊方式处理集合值属性,否则不会生成联结表。
          • 您是否有一种特殊的方式来表示 m:m 关系而不使用联结表?
          • 你能举一个类模型中 m:n 关系的例子吗?
          • 高级示例:interface IGroupable where T:IGroup { ICollection Groups { get; } } interface IGroup where T:IGroupable { ICollection Items { get; } }
          【解决方案9】:

          您的问题与surrogate (or artificial) keys vs natural keys 替代方案密切相关。我认为不是复合键使用较少,而是自然键(无论是复合键还是简单键)不如人工键受欢迎。

          传统的关系数据库理论主要处理“自然”键(从业务领域的角度来看具有意义的键),并且在这种情况下经常会发现复合键......自然。

          但在后来的几年里,数据库设计偏爱(尽管不是唯一)“人工”(代理)键模式,通常是一个没有商业意义的序列号,仅用于唯一标识表中的记录(以及可能是上层的对象)。

          【讨论】:

          • 在使用代理键时仍然会“自然地”找到复合键。代理键与自然键是完全不同的辩论!
          • 阅读问题:“为什么拒绝复合键有利于使用名为 id 的单个主键的所有表。” “名为 id 的单个主键”正是一个代理键。
          • 我仔细阅读了这个问题。他根本没有提到自然键。辩论(自然与替代以及简单与复合)是完全分开的。组合键经常出现在没有自然键的情况下。有关相关示例,请参见例如megocode3.wordpress.com/2008/01/04/…你能想出一个很好的理由给那个联结表一个自己的代理键吗?
          • 首先,替代与自然“辩论”肯定与问题相关(看看接受的答案),并且是海报的强制性阅读。其次,像你的例子这样的联合表肯定有一个“自然”(在这个词的两个意义上)PK,它根本不需要代理键;然而,事实是,现在很多人都有“通用代理 PK 设计”的(好或坏)习惯,对所有表使用“id”PK(代理)(如问题所述);因此,即使在您的示例中,“自然”键也不会用作 PK - 只是一个唯一的限制。
          • 当然,我已经看到了对所有表使用自动编号 PK 的趋势,包括联结表。 OP 也看到了这一点,并在问 为什么
          猜你喜欢
          • 2011-02-07
          • 2011-07-21
          • 2015-07-08
          • 2014-11-22
          • 1970-01-01
          • 1970-01-01
          • 2017-06-09
          • 1970-01-01
          相关资源
          最近更新 更多