选择最好的主键+编号系统

时间:2009-04-01 22:55:08

标签: database database-design primary-key

我们正在尝试为我们正在创建的资产系统建立一个编号系统,在办公室就此主题进行了一些激烈的讨论,所以我决定向SO专家询问。

考虑下面的数据库设计,什么是更好的选择。

alt text

示例1:使用自动代理键。

=================   ==================
Road_Number(PK)     Segment_Number(PK)
=================   ==================
 1                   1

示例2:使用程序生成的PK

=================   ==================
Road_Number(PK)     Segment_Number(PK)
=================   ==================
 "RD00000001WCK"     "00000001.1"

00000001.1表示它是道路的第一个段。每次添加新段时,都会增加,例如00000001.2

示例3:使用两者(添加新列)

=======================    ==========================
ID(PK) Road_Number(UK)     ID(PK)  Segment_Number(UK)
=======================    ==========================
 1     "RD00000001WCK"       1       "00000001.1"

只是一些背景信息,我们将在报告和其他文档中使用Road NumberSegment Number,因此它们必须唯一

我一直都喜欢保持简单,所以我更喜欢示例1,但我一直在读,你不应该在报告/文档中公开你的主键。所以现在我更多地按照例子3的方式思考。

我也倾向于示例3,因为如果我们决定更改资产编号的生成方式,则不必对主键进行级联更新。

您认为我们应该怎么做?

感谢。

编辑:非常感谢大家的回答,给了我很多帮助。

13 个答案:

答案 0 :(得分:62)

这实际上是关于代理(也称为技术或合成)与自然主键的讨论,这是一个被广泛涵盖的主题。我在Database Development Mistakes Made by AppDevelopers中介绍了这一点。

  

自然键是基于的键   外部有意义的数据   (表面上)是独一无二的。常见的例子   是产品代码,两个字母的状态   代码(美国),社会安全号码   等等。代理或技术   主键是那些   绝对没有任何意义   系统。它们是纯粹为了发明而发明的   识别实体并且是   通常是自动递增字段   (SQL Server,MySQL,其他)或   序列(最着名的是Oracle)。

     

在我看来,你应该始终   使用代理键。这个问题有   提出这些问题:

     

自动编号字段是可行的方法。如果您的密钥在数据库之外具有意义(如资产编号),则很可能会发生变化,更改密钥会出现问题。只需将这些内容的索引用于相关表格即可。

答案 1 :(得分:7)

我个人会说保持简单,并使用自动增量主键。如果你需要在程序中显示更多“可读”的东西,那么可能是你的其他想法之一,但我认为这只会给主键字段增加不必要的复杂性。

答案 2 :(得分:7)

我也非常强调“不要将主键用作有意义的数据”阵营。每次我违反这项政策,它都会流下眼泪。有意义的数据迟早需要改变,如果这意味着你必须改变一个主键,它会变得很痛苦。主键可能会在外键约束中使用,您可能会花费多年时间尝试对其进行排序,以便进行简单的数据更改。

我总是在我创建的每个表中使用GUID / UUID作为我的主键,但这只是个人偏好系列或类似的也很好。

答案 3 :(得分:4)

除非......

,否则不要在PK字段中添加含义
  • 100%完全不可能 价值永远不会改变

  • 没有两个人会合理地 争论应该是哪个价值 用于特定行。

使用选项1并将应用中的值格式化为显示时的选项二或三。

答案 4 :(得分:3)

我认为这里要记住的重要一点是数据库/设计中的每个表都可能有多个键。这些是候选键See wikipedia entry for Candidate Keys

根据定义,所有候选键都是相同的。它们都是相关表格的唯一标识符。

然后,您的工作是从候选键池中选择最佳候选者作为主键。主键将由其他表用于建立关系约束,但您可以继续使用候选键来查询表。

由于主键由其他结构引用,因此在连接操作中使用,因此主键选择的标准可归结为以下内容(按重要性顺序):

  • 不可变/稳定 - 主键值不应更改。如果他们这样做,则存在引入更新异常的风险
  • 非空 - 大多数DBMS平台要求主键属性不为空
  • 简单 - 物理存储和性能的简单数据类型和值。整数值在这里运行良好,这是大多数代理/自动生成密钥的首选数据类型

一旦确定了候选密钥,就可以使用上述标准来选择主密钥。如果没有“自然”候选密钥符合标准,则可以创建符合标准的代理密钥,并按其他答案中的说明使用。

答案 5 :(得分:1)

遵循“不使用”政策。

您可能遇到的一些问题:

您需要从多个主机生成密钥。

有人会想要保留连续数字以便一起使用。

人们希望它有多大意义?战争就是为此而战,你已经在第一场小规模战斗中。 “它已经有意义了,如果我们只添加两个数字,我们可以......”即你正在建立一个(应该)可扩展的设计风格。

如果你连接这两个,那么你正在进行类型转换,这可能会弄乱你的查询优化器。

您需要对道路进行重新分类,并重新定义其边界(即移动道路),这意味着更改主键并可能丢失链接。

所有这些都有解决方法,但这是解决方法扩散和失控的问题。除了“简单”之外,它不需要超过一对。

答案 6 :(得分:1)

如前所述,无论平台上最优的数据类型是什么,都要将内部主键保持为关键。

然而,您确实需要让编号系统参数被淘汰,因为这实际上是业务需求,并且可能让它称之为资产的识别系统。

如果只有一个标识符,则将其作为列添加到主表中。如果可能有许多识别系统(并且资产通常有很多),则还需要两个表

    Identifier-type table             Identifier-cross-ref table
      type-id             ------------> type-id              (unique
      type-name                         identifier-string     key)
                                        internal-id


这样,需要访问资产的不同人员可以以自己的方式识别。例如,服务器团队将识别与网络团队不同的服务器,并且与项目管理,帐户等不同。

另外,你可以参加每个人都互相争吵的所有会议。

答案 7 :(得分:0)

要记住的另一件事是,如果您将大量数据导入此系统,您可能会发现Road_Number之类的内容并不像您想象的那样独特,并且可能存在操作障碍解决问题(重新粉刷道路标志等)。

答案 8 :(得分:0)

虽然自然键可能对业务用户有很大的意义,但如果你没有达成协议,那些密钥是神圣的并且不应该被改变,那么你很可能会在保持数据库“必须更改产品代码以适应公司获得的新产品线。“您需要保护数据的RI,并将整数作为具有自动增量的主键是最佳方法。索引和遍历整数而不是char列时,性能也更好。

虽然不适合作为主键,但自然键非常适合用户使用,您可以通过索引强制使用唯一键。它们为数据提供了一个上下文,使各方更容易理解。此外,在您需要重新加载数据的出现时,自然键可以帮助验证您的查找是否仍然有效。

答案 9 :(得分:0)

我会使用代理键,但您可能希望有一个计算列,将代理键“格式化”为更“可读”的值,如果这可以改善您的报告。计算出的列可以从代理键生成示例2,例如用于显示目的。

我认为代理键路由是要走的路,我为它做的唯一例外是连接表,其中主键可以由外键引用组成。即使在这些情况下,我发现拥有代理主键更有用。

答案 10 :(得分:0)

我怀疑你真的应该使用选项#3,正如许多人已经说过的那样。即使有足够的业务密钥,代理PK(整数或GUID)也是很好的做法。代理人将减少维护头痛(正如您自己已经注意到的那样)。

话虽这么说,你可能想要考虑的是你的数据库是否是:

  1. 专注于数据维护和事务处理(即创建/更新/删除操作)
  2. 面向分析和报告(即查询)
  3. 换句话说,用户是关心维护活动数据还是查询大部分静态数据以找到答案?

    如果您非常专注于构建分析和报告数据库(例如数据仓库/市场),这些数据库向技术业务用户(例如报表设计者)公开,他们掌握了业务词汇,那么您可能希望考虑使用基于有意义的业务价值的自然键。它们通过消除对复杂连接的需求帮助降低查询复杂性,并帮助用户专注于他们的任务,而不是与数据库结构作斗争。

    否则你可能专注于一个完整的CRUD DB,它必须在某种程度上涵盖所有基础 - 这是绝大多数情况。在这种情况下,请使用您的选项#3。您可以在将来始终优化可查询性,但您将难以进行可维护性的改进。

答案 11 :(得分:0)

我希望你同意我的观点,即每个设计元素都应该有单一目的。

问题是你认为PK的目的是什么?如果要识别表中的唯一记录,那么代理键就会毫无困难地获胜。这很简单直接。

就选项3中的新列而言,您应该检查是否可以计算这些(最好是在模型层中进行计算,以便比在RDBMS中进行计算时更容易更改)而不会过多其他因素造成的性能损失。例如,您可以将段号和道路号存储在相应的表中,然后使用它们生成“00000001.1”。这样可以即时更改资产编号。

答案 12 :(得分:0)

首先,选项2是绝对最差的选项。作为索引,它是string,这使得它变慢。它是基于业务规则生成的 - 这可能会改变并引起相当大的麻烦。

就个人而言,我总是使用单独的主键列;我总是使用GUID。由于硬盘空间的原因,一些开发人员更喜欢简单的INT而不是GUID。但是,如果出现需要合并两个数据库的情况,GUID几乎不会发生冲突(而INT保证会发生冲突)。

主键应该从不被用户看到。使用户可读不应该是一个问题。主键应该用于链接外键。这是他们的目的。该值应该是机器可读的,一旦创建,就永远不会改变。