【问题标题】:Should I have a dedicated primary key field?我应该有一个专用的主键字段吗?
【发布时间】:2008-10-03 13:08:36
【问题描述】:

我正在设计一个供 Web 应用程序使用的小型 SQL 数据库。

假设一个特定的表有一个名称字段,不允许任何两行具有相同的值。但是,用户可以随时更改名称字段。

此表中的主键将用作其他表中的外键。因此,如果将 Name 字段用作主键,则任何更改都需要传播到其他表。另一方面,唯一性要求将被自动处理。

我的直觉是添加一个整数字段作为主键,它可以由数据库自动填充。拥有这个领域有什么意义,还是会浪费时间?

【问题讨论】:

  • 一个常见的例外是“系统”数据。即您正在定义自己的状态字段等的东西..

标签: database-design primary-key


【解决方案1】:

出于您提到的原因,我自己会使用生成的 PK。此外,按整数索引和比较比按字符串比较快。您也可以在 name 字段上放置唯一索引,而无需将其设为主键。

【讨论】:

  • 这通常是处理主键的公认方式。您永远不应该为您的主键选择一个可以更改其值的列 - 因为您随后会使用它作为火键键对所有表进行级联更新。
【解决方案2】:

您所描述的称为代理键。详细答案请参见Wikipedia article

【讨论】:

    【解决方案3】:

    虽然在整数列上搜索和加入会更快(正如许多人指出的那样),但从不加入会更快。通过存储natural key,您通常可以消除连接的需要。

    对于小型数据库,对外键引用的 CASCADE 更新不会对性能产生太大影响,除非它们非常频繁地更改。

    话虽如此,在这种情况下,您可能应该使用整数或 GUID 作为surrogate key。可按设计更新的主键不是最好的主意,除非您的应用程序有非常令人信服的商业理由要求名称唯一,否则您将不可避免地发生冲突。

    【讨论】:

      【解决方案4】:

      是的 - 根据经验,每张桌子总是如此。

      您绝对不应该将可变字段用作主键,并且在绝大多数情况下您不希望将具有任何其他用途的字段用作主键。

      这是 db 模式的基本良好实践。

      【讨论】:

        【解决方案5】:

        从性能的角度来看,拥有一个整数主键总是一件好事。使用整数主键,您的所有关系都会更有效率。例如,JOIN 会更快 (SQL Server)。

        它还允许您将来修改数据库。很多时候,您有一个唯一的名称列,但后来发现它根本不是唯一的名称。

        现在,您可以通过在列上设置索引来强制列 Name 的唯一性。

        【讨论】:

          【解决方案6】:

          我会使用自动生成的 ID 字段作为主键。连接基于整数 ID 的表比连接文本更容易。此外,如果字段 Name 经常更新,如果它是主键,那么数据库将面临更频繁地更新该字段索引的压力。

          如果字段 Name 始终是唯一的,您仍应在数据库中将其标记为唯一。但是,通常会有两个相同名称的可能性(在您的情况下可能不是现在,但可能在将来),所以我不推荐它。

          使用 ID 的另一个好处是,如果您需要对数据库进行报告。如果您想要针对一组给定名称的报告,即使名称可能发生变化,报告上的 ID 过滤器也会保持一致。

          【讨论】:

            【解决方案7】:

            如果您生活在理论数学家的稀有圈子中(就像 C.Date 一样,因为所有数据值都是已知且正确的),那么主键可以从识别您所指的理想化柏拉图实体的数据组件构建(即姓名+生日+出生地点+父母的姓名),但在混乱的现实世界中可以识别您的现实世界的“合成钥匙”数据库上下文中的实体是一种更实用的做事方式。 (并且可以为空的字段可能非常有用。拿那个吧,关系设计理论的人!)

            【讨论】:

            • 让我们希望 Celko 永远不会成为 Stacker,否则我们将全力以赴。
            • 哦,当这种情况发生时,我们都会变得糟糕!
            【解决方案8】:

            如果您的 name 列将发生变化,那么它就不是一个很好的主键候选者。主键应定义表的唯一行。如果它可以改变它并没有真正做到这一点。在不了解您的系统的更多细节的情况下,我不能说,但这可能是使用代理键的好时机。

            我还将添加它以消除对所有主键使用自动递增整数的神话。使用它们并不总是能提高性能。事实上,很多时候情况恰恰相反。如果您有一个自动递增的列,这意味着系统中的每个 INSERT 现在都有生成新值的额外开销。

            此外,正如 Mark 指出的那样,如果您有一系列相关的表,则在所有表上都有代理 ID,为了从一个表到另一个表,您可能必须将所有这些表连接在一起以遍历它们。使用自然主键通常不是这种情况。用整数连接 6 个表通常比用字符串连接 2 个表要慢。

            当您在所有表上都有自动递增的 ID 时,您也经常失去执行基于集合的操作的能力。您现在必须在游标或其他循环中一次插入一个父行,而不是将 1000 行插入父表,然后将 5000 行插入子表,以便获取生成的 ID,以便您可以分配它们给相关的孩子。我看到一个 30 秒的过程变成了一个 20 分钟的过程,因为有人坚持在数据库中的所有表上使用自动递增的 ID。

            最后(至少出于我在这里列出的原因 - 当然还有其他原因),在所有表上使用自动递增 ID 会导致糟糕的设计。当设计人员不再需要考虑表的自然键可能是什么时,通常会导致错误的重复数据最终出现在数据中。您可以尝试避免唯一索引的问题,但根据我的经验,开发人员和设计人员不会付出额外的努力,并且在使用他们的新系统一年后,他们发现数据是一团糟,因为数据库没有通过自然键对数据进行适当的约束。

            使用代理键当然是有时间的,但在所有表上盲目使用它们几乎总是错误的。

            【讨论】:

            • 请注意,只有当您坚持创建父表、子表、父表、子表时,才需要插入父表和子表的性能损失。只需先创建 1K 父母,然后再创建 5K 孩子。
            【解决方案9】:

            记录的主键必须是唯一的且永久。如果一个记录自然有一个满足这两个条件的简单键,那么就使用它。但是,它们并不经常出现。对于人员记录,人员的姓名既不是唯一的也不是永久的,因此您几乎必须使用自动增量。

            自然键起作用的一个地方是代码表,例如,将状态值映射到其描述的表。给“Active”一个主键 1,给“Delay”一个主键 2 等没有什么意义。 “延迟”、“DLY”; “等待”、“HLD”等。

            另请注意,有人说您应该在字符串上使用整数,因为它们比较快。不是真的。比较两个 4 字节字符字段的时间与比较两个 4 字节整数字段的时间完全相同。更长的字符串当然需要更长的时间,但如果你保持代码简短,那就没有区别了。

            【讨论】:

            • 另一个例子:country_codeUSUK 等)。人们倾向于使用 4 字节的 INT,而 CHAR(2) CHARACTER SET ascii 仅占用 2 字节并且由于其他原因效果更好。当捷克斯洛伐克分裂为捷克共和国和斯洛伐克时,您必须为任一主键设计编写代码。
            【解决方案10】:

            每一行的主键必须是唯一的。 auto_increment Integer 是一个非常好的主意,如果您对填充主键没有其他想法,那么这是最好的方法。

            【讨论】:

              【解决方案11】:

              除了上面所说的,考虑使用 UUID 作为 PK。它将允许您创建跨多个数据库的唯一键。

              如果您需要将数据与其他数据库导出/合并,那么数据将始终保持唯一性,并且可以轻松维护关系。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2011-12-22
                • 2010-10-24
                • 2016-06-30
                • 2023-04-07
                • 1970-01-01
                • 2017-04-03
                • 1970-01-01
                相关资源
                最近更新 更多