字符串作为SQL数据库中的主键


183

我对数据库及其工作原理并不十分熟悉。从性能的角度(插入/更新/查询)角度来看,将字符串用作主键是否比整数慢?

Answers:


196

从技术上讲是可以的,但是如果将字符串作为主键有意义,那么您应该使用它。这一切都取决于要为其创建的表的大小以及将成为主键的字符串的长度(较长的字符串==难以比较)。我不一定会在具有数百万行的表中使用字符串,但是通过在较小的表上使用字符串会导致性能下降,这对于使用不具有整数的整数可能会产生的影响微不足道。与数据无关。


12
它不取决于数据库吗?我认为一个适当索引的字符串从一个数字开始不会慢得多吗?
Ryan Guill

2
我同意要考虑很多变量。(在sqlserver中)我们已经看到了实际的性能问题,即使使用索引时,使用的字符串长度都在十几岁到中高级甚至更高。买对了,例如,有一些东西可以克服这种硬件。
kemiller2002

1
很公平。我同意,但如果字符串有意义,那是您应该使用的字符串。我还要说的是,在数据库中的GUID或UUID字段中肯定存在自动增量字段不起作用的时间。
Ryan Guill

7
另外请记住,进行索引比较时,CHAR和VARCHAR之间通常会有很大的差异
Tom H,2009年

7
该答案的评论数量清楚地表明它是不完整的。提到索引将是最小的可接受答案。
Pedro Rolo

76

使用字符串作为主键的另一个问题是,由于索引不断地按顺序排列,因此当创建一个新键时(该顺序处于中间位置),必须重新排序索引...如果使用自动数字整数,新键刚刚添加到索引的末尾。


2
但是,这可能会导致新插入的“热点”。只要您正确地管理数据库,页面上就应该有额外的空间用于插入,而页面拆分应该很少。
汤姆H,2009年

20
那就是主键集群的时候。您也可以创建非集群的。
学习

XID是有序的,如果您仅使用xid字符串可能会有所帮助
Sinaesthetic

22

插入到具有聚集索引的表中,该插入发生在序列的中间,不会引起索引被重写。这不会导致包含数据的页面被重写。如果该行将在页面上有空间,则将其放置在该页面中。单页将被重新格式化以将行放置在页面的正确位置。当页面已满时,将发生页面拆分,页面上的一半行进入一个页面,另一半行进入另一页面。然后将页面重新链接到页面的链接列表中,该列表包含具有聚集索引的表数据。最多,您最终将编写2页数据库。


很好的解释。但是对所有SQL数据库都适用吗?使用随机UUID作为主键时,我听说过MySQL性能问题。
hgoebl

14

字符串在连接中的速度较慢,并且在现实生活中,它们很少真正地是唯一的(即使应该如此)。唯一的优点是,如果您仅为了获得名称而联接到主表,它们可以减少联接的数量。但是,字符串也经常会发生变化,因此会产生一个问题,当公司名称更改或此人结婚时,必须修复所有相关记录。这可能会严重影响性能,并且如果应该以某种方式关联的所有表都不关联(这种情况比您想象的要频繁发生),那么您也可能会出现数据不匹配的情况。从数据完整性的角度以及从性能的角度来看,在记录的生命周期中始终不变的整数是一个更为安全的选择。自然键通常不太适合维护数据。

我还想指出,两全其美的做法通常是使用自动递增键(在某些特殊情况下为GUID)作为PK,然后在自然键上放置唯一索引。您可以获得更快的联接,没有重复的记录,也不必更新一百万个子记录,因为公司名称已更改。


26
可以很好地用作PK的字符串不能重复-否则它们就不能很好地用作PK。考虑一下ICD-9代码,国家代码,VIN#。用名称作为自然键问题的例子是错误的,因为自然不要将它们作为候选人。
汤姆H

6
@Tom H:ISO县代码确实会更改。[ en.wikipedia.org/wiki/ISO_3166-1#Editions_and_changes ]作为一个相关问题的答案,[ stackoverflow.com/questions/925266/… ]“对于主键,请确保其唯一性在您的控制之下”
Steve Schnepp

4
@SteveSchnepp:是的,ISO是管理该更改的可信赖机构。另一方面,当您需要将递增整数值的单调序列与其他人合并时,您就自己一个人了;)
某天,2012年

1
我同意不要将名称视为关键,我只是看到了很多时候才使用它们。
HLGEM

1
@onedaywhen,很容易通过前缀或后缀来合并2个单调递增整数序列:)
Steve Schnepp 2012年

7

只要它是唯一的,使用什么作为主键都没有关系。如果您关心速度或良好的数据库设计,请使用int,除非您打算复制数据,然后使用GUID。

如果这是一个访问数据库或一些小应用程序,那么谁在乎。我认为我们大多数开发人员都将旧的int或guid放在前面的原因是因为项目对我们有增长的方式,而您想让自己拥有增长的选择。


6

变量太多。它取决于表的大小,索引,字符串键域的性质...

通常,整数会更快。但是差异会足够大吗?很难说。

另外,您选择弦乐的动机是什么?数字自动增量键通常也非常容易。是语义吗?方便?复制/断开连接的问题?您在这里的答案可能会限制您的选择。这也让您想到了您忘记的第三个“混合”选项:指导。


毫无用处,你是什么意思?
HLGEM,

@HLGEM:如果我理解他的写作,他的意思是像将笔记本电脑上创建的记录与主数据库同步。
Joel Coehoorn,

我的意思是我有两个具有相同实体的独立数据库,出于持久性存储目的,只有一个数据库的更新频率较低。如果我查询数据库A上的实体“加利福尼亚”,我希望它与数据库B上的实体基本上是“加利福尼亚”
。– mainstringargs

1
就像同步笔记本电脑中创建的记录一样,这是同样的问题:在一个位置创建的记录不应与在另一个位置创建的记录冲突。一种可能的解决方案是Guid键。
Joel Coehoorn,

5

在您获得一个简单而合理的设计,使其与数据所描述的主题相吻合并且与数据的预期用途非常吻合之前,请不要担心性能。然后,如果出现性能问题,则可以通过调整系统来解决它们。

在这种情况下,最好将字符串作为自然主键使用,前提是您可以信任它。只要是短字符串(例如最多约25个字符),就不要担心它是否是字符串。就性能而言,您不会付出太大的代价。

数据输入人员或自动数据源是否总是为假定的自然键提供值,或者有时将其省略?输入数据偶尔会出错吗?如果是这样,如何检测和纠正错误?

指定查询的程序员和交互式用户是否能够使用自然键来获得所需的信息?

如果您不信任自然键,请发明一个代理。如果您创建了一个代理,您也可能会创建一个整数。然后,您必须担心在用户社区中隐藏代理。一些没有隐藏代理密钥的开发人员开始对此表示遗憾。


3

指数暗示了很多比较。

通常,字符串比整数长,并且可以应用归类规则进行比较,因此与比较整数相比,比较字符串通常是计算量大的任务。

但是,有时候,使用字符串作为主键要比对表进行额外的连接要快string to numerical id


2

是的,但是除非您期望拥有数百万的行,否则不使用基于字符串的键,因为它速度较慢,通常是“过早优化”。毕竟,字符串存储为大数字,而数字键通常存储为小数字。

但是,需要注意的一件事是,如果在任何键上都具有聚集索引,并且正在执行大量在索引中非顺序插入。写入的每一行都将导致索引被重新写入。如果您要进行批量插入,则确实会减慢该过程。


2

对PK列使用整数的两个原因:

  1. 我们可以设置自动递增的整数字段的标识。

  2. 当我们创建PK时,数据库会创建一个索引(集群或非集群),该索引会在数据存储在表中之前对其进行排序。通过在PK上使用标识,优化器无需在保存记录之前检查排序顺序。这样可以提高大表的性能。


1

将字符串用作主键的原因是什么?

我只是将主键设置为自动递增的整数字段,然后在字符串字段上放置索引。

这样,如果您在表上进行搜索,它们应该相对较快,并且所有联接和常规查找都不会影响它们的速度。

您还可以控制要编制索引的字符串字段的数量。换句话说,如果您认为足够就可以说“仅索引前5个字符”。或者,如果您的数据可以相对相似,则可以索引整个字段。


3
我认为将任何智慧都放在钥匙上就是自找麻烦。他们会保持独特性吗?他们是否仅在客户搬家时以州的缩写开头所有帐号。更新字段-没问题-所有通过帐号链接的表格-真是一团糟。
JeffO

1
使用字符串作为PK的示例可以是设置表。例如,settingNamePK,isUserEditable,isCustomerEditable等。然后,如果您想修改设置行为,“更新设置SET ... WHERE settingNamePK ='dailyWorkObligation'”比必须使用ID并将ID的映射存储在某个地方要好得多。当然,您可以拥有一个整数PK,并将设置名称作为另一个唯一键。
MeatPopsicle

主键是一个自动递增的整数,插入是否也不会影响其速度?
丹尼斯

对于好奇的Rails开发人员,这是指定索引长度的方法。请注意,SQLite不支持索引长度。
丹尼斯

1

从性能角度来看-是,与使用整数(PK)达到的性能相比,字符串(PK)会降低性能,其中PK ---> Primary Key。

从需求的角度来看-尽管这不是您问题的一部分,但我还是要提及。当我们在不同的表之间处理大量数据时,通常会寻找可以为特定表设置的可能的键集。这主要是因为有很多表,并且大多数每个表或某个表将通过某种关系彼此关联(外键的概念)。因此,我们确实不能总是选择整数作为主键,而是选择3、4或5个属性的组合作为该表的主键。当我们将记录与其他表关联时,这些键可以用作外键。这使得在需要时在不同表之间关联记录很有用。

因此,为了获得最佳用法-我们始终将1或2个整数与1或2个字符串属性组合在一起,但仅在需要时再组合一次。


0

与数据库中的字符串有关的可能存在很大的误解。几乎每个人都认为数字的数据库表示比字符串更紧凑。他们认为以db-s表示的数字表示为内存中的数字。但事实并非如此。在大多数情况下,数字表示形式更接近于A字符串,就像其他表示形式一样。

使用数字或字符串的速度更多地取决于索引而不是类型本身。


0

默认情况下,ASPNetUserIds是128个字符的字符串,并且性能很好。

如果密钥HAS是在表中是唯一应该是重点。这就是为什么;

主字符串键=正确的数据库关系,1个字符串键(主)和1个字符串索引(主)。

另一种选择是一个典型的INT关键,但如果字符串的HAS是唯一的你仍然可能需要,因为不停的查询添加一个索引来验证或确认其独到之处。

因此,使用int身份密钥=不正确的数据库关系,1个int密钥(主),1个int索引(主),可能是唯一的字符串索引以及手动验证同一字符串不存在(可能像sql检查一样)。

要使用一个int在该主键的字符串,获得更好的性能,当字符串HAS是独一无二的,它必须是一个非常奇怪的情况。我一直喜欢使用字符串键。根据经验,在需要之前不要对数据库进行非规范化。


0

我可能会使用整数作为您的主键,然后将您的字符串(我假设它是某种ID)作为单独的列。

create table sample (
  sample_pk             INT NOT NULL AUTO_INCREMENT,
  sample_id             VARCHAR(100) NOT NULL,
  ...
  PRIMARY KEY(sample_pk)
);

您始终可以在字符串(ID)列(其中sample_id = ...)上进行查询并有条件地进行连接。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.