【问题标题】:What should I consider when selecting a data type for my primary key?为我的主键选择数据类型时应该考虑什么?
【发布时间】:2008-10-23 16:11:55
【问题描述】:

在新建数据库表时,选择主键的数据类型应该考虑哪些因素?

【问题讨论】:

    标签: sql database-design


    【解决方案1】:

    很抱歉,我发现我对相关问题的回答(您可以查看thisthis)可能适用于这个问题。我对它们进行了一点改造......

    您会发现很多帖子都在讨论这个问题,并且您做出的每个选择都有其优点和缺点。这些论点通常是指关系数据库理论和数据库性能。

    在这个问题上,我的观点很简单:代理主键总是有效,而 自然键在这些日子里可能并不总是有效,这有多种原因:字段太短,规则更改等。

    到目前为止,您已经在这里猜到了,我基本上是唯一标识符/代理主键团队的成员,即使我欣赏并理解此处提出的论点,我仍在寻找这样的案例: “自然”键比代理好...

    除此之外,支持这一基本规则的最重要但总是被遗忘的论点之一与代码规范化和生产力有关:

    每次我创建一个表,我会不会浪费时间

    1. 识别其主键及其物理特征(类型、大小)
    2. 每次我想在我的代码中引用它时记住这些特征吗?
    3. 向团队中的其他开发者解释我的 PK 选择?

    我对所有这些问题的回答都是否定的

    1. 当代理选项为我提供了一个防弹的解决方案时,我没有时间浪费在尝试识别“最佳自然主键”上。
    2. 我不想在编写代码时记住我的 Table_whatever 的主键是 10 个字符长的字符串。
    3. 我不想浪费时间来协商自然密钥长度:“好吧,如果你需要 10 个,为什么不选择 12 个为了安全起见?”。这个“在安全的一边”的说法真的让我很生气:如果你想留在安全的一边,那就意味着你真的离不安全的一边不远了!选择代理:它是防弹的!

    所以在过去的五年里,我一直在遵循一个非常基本的规则:每个表(我们称之为“myTable”)都有其第一个字段,称为 'id_MyTable',它属于 uniqueIdentifier 类型。即使此表支持“多对多”关系,其中字段组合提供了一个非常可接受的主键,我更喜欢创建这个 'id_myManyToManyTable' 字段作为唯一标识符,只是为了遵守规则,因为,最后,不疼。

    主要优点是您不必再关心代码中主键和/或外键的使用。获得表名称后,您就知道 PK 名称和类型。一旦您知道在您的数据模型中实现了哪些链接,您就会知道表中可用外键的名称。

    如果您仍然想在表格中的某个位置放置“自然键”,我建议您按照标准模型构建它,例如

    Tbl_whatever
    
       id_whatever, unique identifier, primary key
       code_whatever, whateverTypeYouWant(whateverLengthYouEstimateTheRightOne), indexed
       .....
    

    其中 id_ 是主键的前缀,code_ 用于“自然”索引字段。有些人会争辩说应该将 code_ 字段设置为唯一的。确实如此,并且可以通过 DDL 或外部代码轻松管理它。请注意,许多“自然”键是计算出来的(发票号码),因此它们已经通过代码生成

    我不确定我的规则是不是最好的。但它是一个非常有效的!如果每个人都在应用它,我们将避免浪费时间回答这类问题!

    【讨论】:

    • 这也是我做过的。缺点:(a)有时您需要进行额外的连接,例如你有 invoice_line_item.invoice_id 但你真的想要 invoice_number。 (b) 跨数据库比较可能会很痛苦 (c) 具有少量列的大表的开销 (d) 会影响分区能力
    • 很好的解释 - 我也是这样做的,然后有时会在自然键上添加一个唯一的约束/键。
    【解决方案2】:

    如果使用数字键,请确保数据类型足够大以容纳您可能希望表增长到的行数。

    如果使用 guid,是否需要考虑存储 guid 所需的额外空间?针对 guid PK 进行编码对于应用程序的开发人员或用户来说是否会很痛苦。

    如果使用复合键,您确定组合列始终是唯一的吗?

    【讨论】:

      【解决方案3】:

      我不太喜欢他们在学校教的东西,即使用“自然键”(例如图书数据库中的 ISBN),甚至使用由 2 个或更多字段组成的主键。我永远不会那样做。所以这是我的小建议:

      • 在每个表中始终为您的主键设置一个专用列。
      • 它们在所有表中都应具有相同的列名,即“ID”或“GUID”
      • 尽可能使用 GUID(如果您不需要性能),否则递增 INT

      编辑:
      好的,我想我需要稍微解释一下我的选择。

      • 为您的主键在所有表中使用相同的专用列名称,只会使您的 SQL 语句更易于构建,并且对其他人(可能不熟悉您的数据库布局)更容易去理解。尤其是当你做很多 JOINS 之类的事情时。您无需查找特定表的主键,您已经知道,因为它在任何地方都是相同的。

      • GUID 与 INT 在大多数情况下并不那么重要。除非您达到 GUID 的性能上限或进行数据库合并,否则您不会遇到任何重大问题。 但是我更喜欢 GUID 是有原因的。 GUID 的全局唯一性总有一天会派上用场。也许您现在看不到它的需要,但是诸如将部分数据库同步到笔记本电脑/手机,甚至在不需要知道它们在哪个表中的情况下查找数据记录之类的事情,都是 GUID 优势的很好例子提供。 Integer 仅标识一个表上下文中的记录,而 GUID 标识任何位置的记录。

      【讨论】:

      • 但是如果你使用这种技术,你必须确保在 2 个或更多列上存在 UNIQUE 约束,否则它们可能是主键。
      • 你告诉我们你喜欢什么,但你没有告诉我们为什么
      • 如果您不需要性能(即小表),为什么要使用 GUID 之类的东西?只有当您计划拥有大量数据时,才应真正使用 GUID。
      • 即使你有很多数据——有没有严重溢出 bigint 字段?我认为 GUID 的目的是需要它在其表范围之外是唯一的。
      • 我希望我能多次投票赞成这个答案。您编辑的解释是正确的。
      【解决方案4】:

      在大多数情况下,我使用 identity int 主键,除非场景需要大量复制,在这种情况下我可能会选择 GUID。

      我(几乎)从未使用过有意义的键。

      【讨论】:

        【解决方案5】:

        除非您有一个非常方便的可用自然键,否则请始终使用数字类型的合成(也称为代理)键。即使您确实有可用的自然键,您也可能需要考虑使用合成键并在您的自然键上放置一个额外的唯一索引。考虑一下在联邦法律发生变化时使用社会安全号码作为 PK 的高级数据库发生了什么,转换为合成密钥的成本是巨大的。

        另外,我不同意将每个主键命名为相同的做法,例如“ID”。这使得查询更难理解,而不是更容易。主键应以表命名。例如employee.employee_id、affiliate.affiliate_id、user.user_id 等。

        【讨论】:

        • 我同意第一点。我不同意第二个。但第二个可能不如第一个重要。因此是赞成票。
        • 我同意这两点,但第二点纯粹是品味 - 制表符与空格,/*...*/ 与 //...
        【解决方案6】:

        不要使用浮点数值类型,因为浮点数无法正确比较是否相等。

        【讨论】:

        • 谁会想到使用浮点数作为索引!
        • 我实际上知道至少有三个开发人员曾经尝试过这个。 (当然不是我,因为我很早就读过Code Complete。)仅仅因为某些事情看起来很糟糕并不意味着人们不会这样做。
        【解决方案7】:
        • 你在哪里生成它?递增数字不适合客户端生成的密钥。
          • 您想要一个数据相关或独立的密钥(有时您可以使用业务数据中的 ID,但不能说这是否总是有用)?
          • 这种类型的数据库索引的效果如何?

        到目前为止,我使用了唯一标识符 (GUID) 或递增整数。

        干杯 马蒂亚斯

        【讨论】:

          【解决方案8】:

          在现实世界中有意义的数字通常是个坏主意,因为现实世界时常会改变有关如何使用这些数字的规则,特别是允许重复,然后你就会弄得一团糟你的手。

          【讨论】:

            【解决方案9】:

            我偏爱使用生成的整数键。如果您希望数据库变得非常大,您可以使用 bigint。

            有些人喜欢使用指南。优点是您可以在不更改任何键的情况下合并数据库的多个实例,但缺点是性能可能会受到影响。

            【讨论】:

              【解决方案10】:

              对于“自然”键,任何适合列的数据类型。人工(代理)键通常是整数。

              【讨论】:

                【解决方案11】:

                这一切都取决于。

                a) 您可以将唯一的连续数字作为主键吗?如果是,那么选择 UniqueIdentifier 作为主键就足够了。 b) 如果您的业务需求是需要字母数字主键,那么您必须选择 varchar 或 nvarchar。

                这是我能想到的两个选项。

                【讨论】:

                  【解决方案12】:

                  一个重要因素是您要存储多少数据。我在一家网络分析公司工作,我们有大量数据。因此,由于大小,我们的 pageviews 表上的 GUID 主键会杀死我们。

                  经验法则:为了获得高性能,您应该能够将整个索引存储在内存中。向导很容易打破这个!

                  【讨论】:

                    【解决方案13】:

                    在可以信任的情况下使用自然密钥。自然密钥的某些来源是不可信的。多年前,社会保障局过去常常将同一个 SSN 分配给两个不同的人。他们现在可能已经解决了这个问题。

                    您可能可以相信车辆的 VIN 和书籍的 ISBN(但不能相信可能没有 ISBN 的小册子)。

                    如果您使用自然键,自然键将确定数据类型。

                    如果您不能信任任何自然密钥,请创建合成密钥。为此,我更喜欢整数。为合理扩展留出足够的空间。

                    【讨论】:

                    • “在可以信任的情况下使用自然密钥”:自然密钥不能总是被信任。所以不要使用自然键!
                    • 除非你处理一些玩具的、短期的、一次性的项目——永远不要相信任何自然键。最终自然键失败。有时比任何人预期的要快得多。恰恰相反,代理/合成键是防弹的。根据定义。
                    【解决方案14】:

                    我通常使用所有表的 GUID 列主键(mssql 中的 rowguid)。什么可能是自然键我制作了独特的约束。一个典型的例子是用户必须填写并确保其唯一性的产品标识号。如果我需要一个序列,比如在发票中,我会构建一个表来保存最后一个数字和一个存储过程以确保序列化访问。或者 Oracle 中的序列 :-) 我讨厌自然键的“社会安全号码”样本,因为该号码在注册过程中永远不会出现。导致需要一个生成虚拟数字的方案。

                    【讨论】:

                      【解决方案15】:

                      我通常总是使用整数,但这里有一个有趣的观点。

                      https://blog.codinghorror.com/primary-keys-ids-versus-guids/

                      【讨论】:

                        【解决方案16】:

                        尽可能尝试使用作为自然键的主键。例如,如果我有一个每天记录一条记录的表,那么 logdate 将是一个很好的主键。否则,如果没有自然键,只需使用 int。如果您认为您将使用超过 20 亿行,请使用 bigint。有些人喜欢使用 GUID,这很好用,因为它们是独一无二的,而且你永远不会用完空间。但是,如果您只是进行临时查询,它们不必要地冗长,并且难以输入。

                        【讨论】:

                        • 我不喜欢这样,因为业务规则发生了变化。也许将来您需要每小时记录一条记录,因为事情变得更加繁忙,或者每个商店位置记录一条记录而不是一条记录。
                        • 好点辛迪。此外,除 GUID 之外的任何内容都无法复制。
                        • 我认为应该反过来:代理键必须是首选;只有当有一些非常非常强烈的理由时才可以使用自然键。根据我自己的经验,只有在玩具的一次性项目中才能证明自然键是合理的......
                        猜你喜欢
                        • 2016-10-13
                        • 1970-01-01
                        • 2011-02-14
                        • 2010-10-13
                        • 2020-02-18
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2022-01-24
                        相关资源
                        最近更新 更多