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

时间:2010-10-13 21:18:17

标签: sql database relational-database relational-model

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

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

我们需要使用自动递增键吗?

9 个答案:

答案 0 :(得分:20)

答案 1 :(得分:16)

没有。

在大多数情况下,使用代理INT IDENTITY密钥是一个简单的选择:它可以保证为NOT NULL且100%唯一,许多“自然”密钥不提供 - 名称可以更改SSN和其他信息项也是如此。

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

主键必须

  • 独特(100%保证!不只是“几乎”独特)
  • NON NULL

主键应该是:

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

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

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

答案 2 :(得分:3)

我认为在短语“Primary”Key中使用“Primary”这个词是真正意义上的,具有误导性。

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

然后,任何密钥都有几个通常相互矛盾的目的。

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

目的4 。 ( 从数据一致性角度来看最重要 !)通过防止表示相同逻辑实体的重复行插入表来确保数据一致性。 (这通常称为“自然”键,应该包含相对不变的表(实体)属性。)

显然,任何非有意义的非自然键(如GUID或自动生成的整数 完全 无法满足目的4。

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

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

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

答案 3 :(得分:2)

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

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

答案 4 :(得分:1)

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

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

答案 5 :(得分:1)

不,绝对没有。

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

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

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

编辑:

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

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

答案 6 :(得分:1)

我认为有两件事可以解释有时使用自动递增键的原因:

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

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

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

答案 7 :(得分:0)

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

答案 8 :(得分:0)

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

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

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