首页 文章

每个表真的需要一个自动递增的人工主键吗? [关闭]

提问于
浏览
13

我在7年的开发经验中看到的每个数据库中的几乎每个表都有一个自动递增的主键 . 为什么是这样?如果我有一个美国州的表格,其中每个州的每个州都必须有一个唯一的名称,那么自动递增主键的用途是什么?为什么不使用州名作为主键?对我来说似乎是一个允许重复伪装成唯一行的借口 .

这对我来说显而易见,但是再一次,似乎没有其他人像我一样到达并采取相同的逻辑结论,所以我必须假设我错了 .

我们需要使用自动递增键是否有任何真实的实际原因?

9 回答

  • 0

    没有 .

    在大多数情况下,拥有一个代理 INT IDENTITY 键是一个简单的选择:它可以保证是NOT NULL和100%唯一,很多"natural"键和其他信息项 .

    在状态缩写和名称的情况下 - 如果有的话,我将使用两个字母的州缩写作为键 .

    主键 must be

    • 独特(100%保证!不仅仅"almost"独特)

    • 非空

    主键应该是:

    如果可能的话

    • 稳定(不改变 - 或至少不太频繁)

    国家双字母代码肯定会提供这个 - 这可能是自然键的候选者 . 密钥也应该很小 - 一个4字节的INT是完美的,两个字母的CHAR(2)列是相同的 . 我会 not 曾经使用VARCHAR(100)字段或类似的东西作为关键 - 它太笨重,很可能会一直改变 - 不是一个好的关键候选者 .

    因此,虽然您不必拥有自动递增的“人工”(代理)主键,但它通常是一个很好的选择,因为没有自然发生的数据真正取决于成为主键的任务,并且您希望避免使用具有多个列的巨大主键 - 这些只是过于笨重且效率低下 .

  • 16

    This question has been asked numerous times on SO and has been the subject of much debate over the years amongst (and between) developers and DBAs.

    首先让我说,你问题的前提意味着一种方法普遍优于另一种方法......现实生活中很少出现这种情况 . Surrogate keys and natural keys both have their uses and challenges - 它's important to understand what they are. Whichever choice you make in your system, keep in mind there is benefit to consistency - it makes the data model easier to understand and easier to develop queries and applications for. I also want to say that I tend to prefer surrogate keys over natural keys for PKs ... but that doesn' t意味着自然键有时在该角色中无用 .

    It is important to realize that surrogate and natural keys are NOT mutually exclusive - and in many cases they can complement each other . 请记住,数据库表的"key"只是唯一标识记录(行)的东西 . 单行完全有可能具有多个键,这些键表示使记录唯一的不同约束类别 .

    A primary key, on the other hand 是数据库用于强制引用完整性和在其他表中表示外键的特定唯一键 . 任何表只能有一个主键 . 主键的 essential quality 是100%唯一且非NULL . 主键的 desirable quality 是稳定的(不变) . 虽然可变主键是可能的 - 但它们会导致数据库出现更多问题(级联更新,RI失败等) . 如果您确实选择为表使用代理主键 - 您还应考虑创建唯一约束以反映任何自然键的存在 .

    Surrogate keys are beneficial in cases where:

    • 自然键不稳定(值可能会随时间变化)

    • 自然键很大或很笨重(多列或长值)

    • 自然键可以随时间变化(随时间添加/删除列)

    By providing a short, stable, unique value for every row, we can reduce the size of the database, improve its performance, and reduce the volatility of dependent tables which store foreign keys. 关键polymorphism也有好处,我稍后会介绍 .

    In some instances, using natural keys to express relationships between tables can be problematic . 例如,假设您有一个PERSON表,其自然键是 {LAST_NAME, FIRST_NAME, SSN} . 如果您有一些其他表GRANT_PROPOSAL,您需要存储对Proposer,Reviewer,Approver和Authorizer的引用,会发生什么 . 您现在需要12列来表达此信息 . 您还需要提出某种命名约定,以确定哪些列属于哪种类 . 但是,如果您的PERSON表需要6个,8个或24个列作为自然键,该怎么办?这很快变得无法管理 . Surrogate keys resolve such problems by divorcing the semantics (meaning) of a key from its use as an identifier .

    我们还来看一下您在问题中描述的示例 .

    是否应将状态的2个字符缩写用作该表的主键 .

    从表面上看,缩写字段看起来符合良好主键的要求 . 它's relatively short, it is easy to propagate as a foreign key, it looks stable. Unfortunately, you don' t控制缩写的集合......邮政服务 . 这是一个有趣的事实:1973年,USPS将内布拉斯加州的缩写从NB改为NE尽量减少与加拿大新不伦瑞克省的混淆 . The moral of the story is that natural keys are often outside of the control of the database ... and they can change over time. Even when you think they cannot . 对于像人或产品等更复杂的数据,这个问题更加明显 . 随着业务的发展,使这些实体成为唯一的定义可能会发生变化 . 这可能会给数据建模人员和应用程序开发人员带来严重问题 .

    Earlier I mentioned that primary keys can support key polymorphism. 这是什么意思?好吧,多态性是一种类型A能够像另一种类型一样出现和使用的能力B.在数据库中,这个概念指的是将来自不同类实体的键组合成单个表的能力 . 我们来看一个例子 . 想象一下,您希望系统中有一个审计跟踪,用于标识哪个用户在哪个日期修改了哪些实体 . 创建一个包含字段的表格会很好: {ENTITY_ID, USER_ID, EDIT_DATE} . 不幸的是,使用自然键,不同的实体具有不同的键 . 所以现在我们需要为每种实体创建一个单独的linking table并以一种理解不同类型实体及其键形状的方式构建我们的应用程序 .

    Don't get me wrong. I'm not advocating that surrogate keys should ALWAYS be used. In the real world never, ever, and always are a dangerous position to adopt . 代理键的最大缺点之一是它们可能导致表具有由大量"meaningless"数字组成的外键 . 这可能会使解释记录含义变得很麻烦,因为您必须加入或查找其他表中的记录才能获得完整的图片 . 它还可以使分布式数据库部署更加复杂,因为并不总是可以跨服务器分配唯一的递增数字(尽管大多数现代数据库如Oracle和SQLServer通过序列复制来缓解这种情况) .

  • 1

    我认为在“主要”键中使用“主要”一词是真正意义上的,具有误导性 .

    首先,使用“key”是表中必须唯一的属性或属性集的定义,

    然后,拥有任何密钥服务于几个通常相互矛盾的目的 .

    Purpose 1. 将连接条件用作子表中与该父表有关系的一个或多个记录 . (在这些子表中明确或隐式定义外键)
    Purpose 2. (相关)确保子记录必须在父表中具有父记录(子表FK必须作为父表中的键存在)
    Purpose 3 . 提高需要快速查找表中特定记录/行的查询的性能 .

    Purpose 4 . ( Most Important from data consistency perspective !)通过防止表示相同逻辑实体的重复行插入表来确保数据一致性 . (这通常称为"natural"键,应该包含相对不变的表(实体)属性 . )

    显然,任何非有意义的非自然键(如GUID或自动生成的整数)都不能满足目的4 .

    但通常,对于许多(大多数)表,可以提供#4的完全自然的键通常由多个属性组成,并且过宽,或者如此宽,以至于将其用于#1,#2或#3目的将导致不可接受性能后果 .

    答案很简单 . 使用两者 . 对其他子表中的所有Joins和FK使用简单的自动生成整数键,但要确保每个需要数据一致性的表(很少有表没有)具有备用的自然唯一键,以防止插入不一致的数据行 . ..另外,如果你总是同时使用两者,那么所有反对使用自然键的反对意见(如果它改变了怎么办?我必须改变它被引用为FK的每个地方)都没有实际意义,因为你没有使用它 . ..你只是在一个表中使用它,它是一个PK,以避免不一致的duplciate数据...

    没有两者的唯一一次离开是一个完全独立的表,它与其他表没有任何关系,并且具有明显可靠的自然键 .

  • 1

    通常,数字主键的性能优于字符串 . 您可以另外创建唯一键以防止重复进入 . 这样您就可以确保没有重复,但您也可以获得数字的性能(与场景中的字符串相比) .

    在所有可能的情况下,主要数据库对基于字符串的主键不存在的基于整数的主键进行一些性能优化 . 但是,这只是一个合理的猜测 .

  • 0

    是的,在我看来,每个表都需要一个自动递增的整数键,因为它可以同时进行JOIN和(特别是)前端编程更容易,更容易 . 其他人则有不同的看法,但这是20多年的经验 .

    唯一的例外是小“代码”或“查找”表,其中我愿意替换短(4或5个字符)TEXT代码值 . 我这样做是因为我经常在我的数据库中使用很多这些,它允许我向用户呈现有意义的显示,而无需在查找表中查找描述或将其加入到结果集中 . 您的States表示例适合此类别 .

  • 1

    不,绝对不是 .

    拥有一个无法更改的主键是一个好主意(UPDATE对于主键列是合法的,但通常可能会造成混淆,并且可能会为子行创建问题) . 但是如果你的应用程序有一些比自动递增值更合适的候选者,那么你应该使用它 .

    性能方面,通常更少的列更好,特别是更少的索引 . 如果您有另一列具有唯一索引并且任何业务流程永远不能更改,那么它可能是合适的主键 .

    从MySQL(Innodb)的角度来看,使用“真实”列作为主键而不是“人工”列也是一个好主意,因为InnoDB总是将主键聚类并将其包含在二级索引中(即如何它找到了它们中的行) . 这使得它有可能使用主键进行有用的优化,而主键不能与任何其他唯一索引一起使用 . MSSQL用户通常选择对主键进行群集,但它也可以群集不同的唯一索引 .

    编辑:

    但是如果它是一个小型数据库并且您并不太关心性能或大小,那么添加一个不必要的自动增量列并不是那么糟糕 .

    非自动递增值(例如UUID,或根据您自己的算法生成的某些其他字符串)可能对分布式,分片式或多样化系统很有用,因为维护一致的自动递增ID很困难(或者不可能) - 考虑分布式系统继续在网络分区的两侧插入行) .

  • 3

    我认为有两件事可以解释为什么有时会使用自动递增键:

    • 空间考虑;确定你的州名不是很多,但它所占用的空间可能会增加 . 如果你真的想以状态名称作为主键存储状态,那么继续,但它会占用更多的位置 . 在某些情况下这可能不是问题,这听起来像是过去的问题,但这种习惯可能是根深蒂固的 . 我们程序员和DBA都喜欢这些习惯:D

    • 防御性考虑:我最近遇到了以下问题;我们在数据库中有用户,其中电子邮件是所有身份识别的关键 . 为什么不将电子邮件作为主要密钥?除了突然的边境案件蔓延,其中一个人必须在那里两次有两个不同的地址,没有人在规格中谈论它,所以地址没有规范化,并且在这种情况下,两个不同的电子邮件必须指向同一个人和 . ..过了一会儿,你停止拔出你的头发并添加该死的整数id列

    我不是说这是一个坏习惯,也不是一个好习惯;我确信可以围绕合理的主键设计好的系统,但这两点让我相信恐惧和习惯是罪魁祸首中的两个

  • 2

    它是关系数据库的关键组件 . 拥有一个与状态相关的整数而不是整个州名会在数据库中保存一堆空间!想象一下,你有一百万条记录引用你的状态表 . 您是否希望在每个记录上使用4个字节作为数字,或者您是否要为每个州名使用整个字节数?

  • 20

    以下是一些实际考虑因素 .

    当有一个整数列作为主键时,大多数现代ORM(rails,django,hibernate等)效果最佳 .

    此外,具有标准命名约定(例如,id作为主键,table_name_id作为外键)使得识别键更容易 .

相关问题