我们正在尝试为我们正在创建的资产系统建立一个编号系统,在办公室就此主题进行了一些激烈的讨论,所以我决定向SO专家询问。
考虑下面的数据库设计,什么是更好的选择。
示例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 Number
和Segment Number
,因此它们必须唯一。
我一直都喜欢保持简单,所以我更喜欢示例1,但我一直在读,你不应该在报告/文档中公开你的主键。所以现在我更多地按照例子3的方式思考。
我也倾向于示例3,因为如果我们决定更改资产编号的生成方式,则不必对主键进行级联更新。
您认为我们应该怎么做?
感谢。
编辑:非常感谢大家的回答,给了我很多帮助。答案 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
根据定义,所有候选键都是相同的。它们都是相关表格的唯一标识符。
然后,您的工作是从候选键池中选择最佳候选者作为主键。主键将由其他表用于建立关系约束,但您可以继续使用候选键来查询表。
由于主键由其他结构引用,因此在连接操作中使用,因此主键选择的标准可归结为以下内容(按重要性顺序):
一旦确定了候选密钥,就可以使用上述标准来选择主密钥。如果没有“自然”候选密钥符合标准,则可以创建符合标准的代理密钥,并按其他答案中的说明使用。
答案 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)也是很好的做法。代理人将减少维护头痛(正如您自己已经注意到的那样)。
话虽这么说,你可能想要考虑的是你的数据库是否是:
换句话说,用户是关心维护活动数据还是查询大部分静态数据以找到答案?
如果您非常专注于构建分析和报告数据库(例如数据仓库/市场),这些数据库向技术业务用户(例如报表设计者)公开,他们掌握了业务词汇,那么您可能希望考虑使用基于有意义的业务价值的自然键。它们通过消除对复杂连接的需求帮助降低查询复杂性,并帮助用户专注于他们的任务,而不是与数据库结构作斗争。
否则你可能专注于一个完整的CRUD DB,它必须在某种程度上涵盖所有基础 - 这是绝大多数情况。在这种情况下,请使用您的选项#3。您可以在将来始终优化可查询性,但您将难以进行可维护性的改进。
答案 11 :(得分:0)
我希望你同意我的观点,即每个设计元素都应该有单一目的。
问题是你认为PK的目的是什么?如果要识别表中的唯一记录,那么代理键就会毫无困难地获胜。这很简单直接。
就选项3中的新列而言,您应该检查是否可以计算这些(最好是在模型层中进行计算,以便比在RDBMS中进行计算时更容易更改)而不会过多其他因素造成的性能损失。例如,您可以将段号和道路号存储在相应的表中,然后使用它们生成“00000001.1”。这样可以即时更改资产编号。
答案 12 :(得分:0)
首先,选项2是绝对最差的选项。作为索引,它是string
,这使得它变慢。它是基于业务规则生成的 - 这可能会改变并引起相当大的麻烦。
就个人而言,我总是使用单独的主键列;我总是使用GUID。由于硬盘空间的原因,一些开发人员更喜欢简单的INT而不是GUID。但是,如果出现需要合并两个数据库的情况,GUID几乎不会发生冲突(而INT保证会发生冲突)。
主键应该从不被用户看到。使用户可读不应该是一个问题。主键应该用于链接外键。这是他们的目的。该值应该是机器可读的,一旦创建,就永远不会改变。