使用电子邮件地址作为主键?


234

与自动递增的数字相比,电子邮件地址是否是主要的候选地址?

我们的Web应用程序需要电子邮件地址在系统中唯一。因此,我想到了使用电子邮件地址作为主键。但是我的同事建议,字符串比较将比整数比较慢。

不使用电子邮件作为主键是正确的理由吗?

我们正在使用PostgreSQL


5
您所说的“主要”是什么意思?如果电子邮件地址需要唯一,则它是密钥,并且需要唯一约束。除非有实际原因,例如优化性能不佳的系统,否则是否决定“升级”为“主要”是任意的。
2010年

7
如果要数据库强制使用唯一的电子邮件地址,请创建具有唯一索引的列,但不要将其用作主键。
詹姆斯·韦斯特盖特

104
@robert如果有人要更改他的电子邮件地址怎么办?您还要更改所有外键吗?
systempuntoout 2010年

3
@onedaywhen-几乎没有任何区别,但是默认情况下主键将被聚类,而唯一索引则不会。您仍将需要定义主键,该键将是默认的单记录查找键,唯一索引仅在普通索引上强制了列的唯一性
James Westgate

3
@James Westgate:仅供参考,PostgreSQL中没有自动集群之类的东西。在所有字段都不为空的情况下,PRIMARY KEY在磁盘上实现的方式与UNIQUE INDEX完全相同。
马修·伍德

Answers:


283

字符串比较比int比较慢。但是,如果您只是使用电子邮件地址从数据库中检索用户,这并不重要。如果您有包含多个联接的复杂查询,这很重要。

如果您在多个表中存储有关用户的信息,则用户表的外键将是电子邮件地址。这意味着您多次存储了电子邮件地址。


11
@Sjoerd:问题不在于电子邮件地址被多次存储,尽管这绝对效率不高,但是今天谁在乎硬盘空间。大多数企业没有google规模,这很重要。问题在于电子邮件地址之后无法更改,因为它既是主键又被称为外键。
Stefan Steiger,2015年

@StefanSteiger谁说了硬盘空间?您存储的所有内容都会占用RAM中的空间。
乔纳森·艾伦2015年

如果有人像我一样想知道,那么GUID密钥将等同于我认为的电子邮件密钥。
tofutim

178

我还要指出,电子邮件是创建唯一字段的错误选择,有些人甚至是小型企业都共享电子邮件地址。就像电话号码一样,电子邮件也可以重复使用。一年后,Jsmith @ somecompany.com可以轻松属于John Smith,两年后可以轻松属于Julia Smith。

电子邮件的另一个问题是它们经常更改。如果您要使用其他键加入其他表,那么您还必须更新其他表,这在整个客户公司更改其电子邮件时都会对性能造成很大的影响(我见过这种情况)。


47
+1代表级联更新问题。这就是为什么朋友让朋友只使用代理键;-)的原因。
sleske

10
啊,我一点都不喜欢这样的说法...替代键也可能是问题的根源;是的,该应用程序对于更改业务和/或完整性规则将更加健壮,但是信息可能会更容易丢失,并且记录的身份也变得不清楚。因此,我建议您在这里使用经验法则……
Unreason

12
@onedaywhen和@jay,只是因为您认为它应该唯一而不会使它唯一。是的,夫妻可能是不同的顾客。只是因为您之前没有遇到过这并不意味着它不会发生。我碰到了它,并且确实发生了,这就是为什么无论您认为是否应该都不应该将电子邮件视为唯一的原因。您回退这种要求是因为它本质上是错误的。
HLGEM 2010年

15
@HLGEM:我不想陷入无休止的争论,但是您不能说,在不了解上下文的情况下,基于假设的提议密钥并不是唯一的。例如,从电话公司的角度来看,根据定义,电话号码可以唯一地标识客户。是的,您可以说:“但是,如果有两个或三个人在您拨打该号码时可能会回答?” 但这无关紧要。从电话公司的角度来看,按照定义,这是一个客户。(续...)
杰伊

14
(续)同样,如果您要构建一个主要与电子邮件通信有关的系统(可能是消息分发系统或通知转发系统),那么根据定义,电子邮件地址很可能会唯一标识一个用户。如果多个人共享该电子邮件地址,则无关紧要。它们是单个消息目标,因此,它们是单个用户。“用户”和“客户”不必是“个人”的同义词。
杰伊2010年

99

主键应唯一恒定

电子邮件地址会随着季节的变化而变化。用作查找的辅助键,但为主键的选择不理想。


17
好的密钥的一个特性是应该稳定,但不一定是不变的。
2010年

5
@onedaywhen:是的!否则,为什么SQL支持级联更新?
Bill Karwin

18
如果您有选择,请选择恒定/不变键;减少您日后的工作;仅仅因为SQL支持级联更新并不意味着它总是一个好主意!
Steven A. Lowe 2010年

7
@Vincent Malgrat:“级联更新...制动了db规范化”-方法使您误解了规范化的概念!
2010年

5
@Vincent Malgrat:感谢您确认您确实误解了标准化的概念。“您不应该在多行上重复相同的信息” –您真的要说“信息”吗?复合键通常将包含在多行上重复的值。对于外键,值是引用而不是“重复”的,差异很大。具有两个值(例如,“是”和“否”)的单列域如果具有三行或更多行,则在引用表中的多行上将具有相同的值。这真的是基本的东西!
2010年

64

使用电子邮件地址作为主键的缺点:

  1. 加入时变慢。

  2. 现在,任何其他带有已发布外键的记录都将具有更大的值,从而占用更多的磁盘空间。(考虑到今天的磁盘空间成本,这可能是一个琐碎的问题,但就目前而言,读取记录需要更长的时间。请参阅#1。)

  3. 电子邮件地址可能会更改,这将强制更新所有使用此地址作为外键的记录。由于电子邮件地址不会经常更改,因此性能问题可能很小。更大的问题是您必须确保提供它。如果您必须编写代码,则这需要更多工作,并且可能会出现错误。如果您的数据库引擎支持“按更新级联”,则是一个小问题。

使用电子邮件地址作为主键的优点:

  1. 您也许可以完全消除一些联接。如果您仅需要“主记录”中的电子邮件地址,则必须使用抽象整数键进行联接才能检索它。如果密钥是电子邮件地址,则您已经拥有它,因此不需要加入。这是否对您有帮助,取决于这种情况出现的频率。

  2. 当您进行临时查询时,人们很容易看到正在引用的主记录。尝试查找数据问题时,这可能是一个很大的帮助。

  3. 几乎可以肯定,无论如何,您都将在电子邮件地址上需要一个索引,因此使其成为主键可以消除一个索引,从而提高了插入的性能,因为它们现在仅需要更新一个索引而不是两个索引。

以我的拙见,这也不是灌篮高手。我倾向于在可用的自然键时使用自然键,因为它们更易于使用,并且缺点在大多数情况下并没有太大关系。


@Conrad:尽管,他确实指出,如果您的引擎支持ON UPDATE CASCADE,则它不是PITA。在代码方面,这不是问题;唯一真正的问题是更新的范围有多大,密钥的范围有多大。电子邮件地址可能有点多,但是2个字符的国家/地区代码PK的CASCADE UPDATE没什么大不了的。
马修·伍德

5
@马修恕我直言,它仍然是皮塔饼。例如,假设在设计国家/地区表时只有两个表引用了该表,没有任何重复,但是随着时间的推移,它变成了20个表,每个表都有成千上万的记录。有些带有参考,有些没有。这使得单个逻辑写最终成为数以万计的写操作,并且并不能对所有表进行写操作,因为有人在添加表时忘记了引用。这是我在2个字符的国家/地区代码表上发生的确切事情,我没有告诉你。
康拉德·弗里克斯

@Wood&Conrad:最糟糕的情况是没有内置的数据库支持。然后,您必须为每个表编写代码,并带有已发布的引用,这只是一个痛苦,也为错误的插进了大门。对于级联,您只需要记住在每个表上添加一个子句即可,很大。
杰伊(Jay)

2
优势1和3是过早的优化,优势2是次要的优势,任何体面的查询工具都可以完全克服。
Ash Ash's

4
@Ash:您是“优化”和“过早优化”之间的区别。但是,基于同样的理由,我见过任何人提到的所有缺点都是过早的优化。那那会留在哪里呢?至于#2,我发现在尝试进行临时查询时输入额外的联接是一个主要难题。记录通常具有多个外键,因此您可能需要几次联接才能获得可理解的数据。如果用“体面的查询工具”来表示您想要在不通知您的情况下找出想要查看的数据,然后神奇地为您进行联接,那么我想看看它是如何工作的。
杰伊(Jay)

12

真不好 假设某些电子邮件提供商停业了。然后,用户将要更改其电子邮件。如果您已使用电子邮件作为主键,则用户的所有外键都将复制该电子邮件,这真是很难更改...

...而且我什至还没有开始谈论性能方面的考虑。


更改电子邮件地址将如何导致重复?除非用户A更改了他的电子邮件地址,然后用户B更改了他的电子邮件使其与用户A的旧值相同,并且您的更新未按顺序进行。我想这是遥不可及的。
杰伊,2010年

2
根据定义,外键引用包含它所引用的行的主键的值。换句话说,它复制了主键的值。(因此,复制不是由更改值引起的。但是由于此复制以及强制执行该复制的约束,因此更改更加困难)。
优点

5
为“假设某些电子邮件提供商停业”这一行的+1。
Reddy 2010年

这不是问题。存在外键级联以解决此问题。如果用户更改了他们的电子邮件,该更改将级联到所有表,并将其用作外键。
拉法

1
@rafa,我向您保证,如果您使用级联更新,并且整个提供商都停业了或更改了他们的名字(Yahoo.com成为HooYa.com),那么在级联时,您的数据库将被锁定所有用户数小时甚至数天通过系统。这是一个非常有效的问题(如果您有大量数据,并且密钥可能会更改,那么使用级联更新是一个糟糕的主意。)
HLGEM 2013年

12

我不知道这可能不是您设置中的问题,但是取决于您的RDBMS,列的值可能区分大小写。PostgreSQL文档说:“如果将一列声明为UNIQUE或PRIMARY KEY,则隐式生成的索引区分大小写”。换句话说,如果您接受用户输入以电子邮件作为主键的表中的搜索,并且用户提供“ John@Doe.com”,则不会找到“ john@doe.com”。


7
在这方面值得一提的是,John @ Doe.com和john@Doe.com可能是同一个邮箱,也可能是不同的邮箱,而且您无话可说-规范中没有任何内容可以说明本地部分是否区分大小写-敏感。
TELENT

对于电子邮件地址的唯一性实施,这是一个更普遍的问题,而不是是否应将它们用作主键-两种方法都存在相同的问题。+1是因为它仍然是一个非常有用的点

11

似乎没有人提到电子邮件地址可能被视为私有的可能问题。如果电子邮件地址是主键,则配置文件页面URL很可能看起来像..../Users/my@email.com。如果您不想公开用户的电子邮件地址怎么办?您必须找到其他识别用户的方式,可能是通过唯一的整数值来创建类似的URL ..../Users/1。然后,您最终将获得唯一的整数值。


9

逻辑上讲,电子邮件是自然键。在物理级别上,假设您使用的是关系数据库,那么自然键和主键就不太适合。原因主要是其他人提到的性能问题。

因此,可以对设计进行调整。自然键成为备用键(UNIQUE,NOT NULL),并且您使用替代/人造/技术键作为主键,在您的情况下可以自动递增。

systempuntoout问,

如果有人想更改他的电子邮件地址怎么办?您还要更改所有外键吗?

这就是级联的目的。

使用数字代理键作为主键的另一个原因与平台中索引的工作方式有关。例如,在MySQL的InnoDB中,表中的所有索引都预先添加了主键,因此您希望PK尽可能小(出于速度和大小的考虑)。同样与此相关的是,当按主键顺序存储时,InnoDB更快,而字符串则无济于事。

使用字符串作为备用键时要考虑的另一件事是,使用所需的实际字符串的哈希值可能会更快,从而跳过了某些字母的大写和小写之类的事情。(实际上,我在寻找参考信息以确认我刚才所说的内容时落在了这里;仍在寻找...)


4

是的,最好使用整数代替。您还可以将电子邮件列设置为唯一约束。

像这样:

CREATE TABLE myTable(
    id integer primary key,
    email text UNIQUE
);

8
为什么它“更好”?任何原因或来源?
Sjoerd 2010年

20
您能详细说明一下吗?
Sjoerd 2010年

4

是的,这是一个错误的主键,因为您的用户将要更新其电子邮件地址。


1
想我会指出,现在我们有级联,这是不是一个问题
malhal

3

整数主键更好的另一个原因是当您在其他表中引用电子邮件地址时。如果地址本身是主键,则必须在另一个表中将其用作键。因此,您可以多次存储电子邮件地址。


3

我对postgres不太熟悉。主键是一个大话题。我在此网站(stackoverflow.com)上看到了一些很棒的问题和解答。

我认为您可以通过使用数字主键并在电子邮件列上使用UNIQUE INDEX来获得更好的性能。电子邮件的长度往往会有所不同,并且可能不适用于主键索引。

一些在这里这里阅读


3

就个人而言,在设计数据库时,我不使用任何信息作为主键,因为以后很可能需要更改任何信息。我提供主键的唯一原因是,从客户端执行大多数SQL操作很方便,而我的选择始终是自动递增整数类型。


2

您的同事是正确的:将自动递增的整数用作主键。

您可以在应用程序级别实现电子邮件唯一性,也可以将电子邮件地址列标记为唯一,然后在该列上添加索引。

仅在将字段插入表中时,将字段添加为唯一字段会花费字符串比较的费用,而在执行联接和外键约束检查时则不会。

当然,您必须注意,在数据库级别向应用程序添加任何约束都会导致应用程序变得不灵活。在将任何字段设置为“唯一”或“不为空”之前,请务必给予应有的考虑,因为您的应用程序需要该字段是唯一或非空的。


1
“在实施需求x之前,总是仅出于您的应用程序需要需求x的考虑。” -我已经读了很长时间的最坏建议。
2010年

我对您的“论据”不满意-在现实生活中,经常会出现一些无法立即获得一些基本数据(例如电话号码)的情况。如果在数据库中将该字段标记为NOT NULL,则将要求用户使用伪字段(例如123)来污染数据,而不是将其保留为空。让应用程序处理约束会更实际(在这种情况下,应用程序可以将一个空字段标记为操作项)。
jrharshath

5
我同意定义字段“ not null”应该谨慎。应该仔细考虑“我们总是需要客户的电话号码”之类的要求。即使我们现在不知道电话号码,然后有时再回去获取,有时创建客户记录还是不理想的吗?但是“此字段必须唯一”是不同的类别。我简直无法想象说:“两个雇员拥有相同的社会保险号是可以的,我们稍后会解决。” 您将如何理顺数据?
杰伊2010年

1
做狼:我曾经认识一个没有自己电话号码的女人。那你怎么办呢?
David Thornley,2010年

@DavidThornley听起来您应该多做些事,或者改掉更友好的举止。
菲利普·希夫

2

将GUID用作主键...这样,您可以在执行INSERT时从程序中生成它,而无需从服务器获取响应即可知道主键是什么。它在表和数据库中也将是唯一的,并且您不必担心如果某天截断表并将自动增量重置为1会发生什么情况。


2
除非您几乎不关心性能,否则请使用GUID。如果您要构建一个需要扩展的系统,那就不是第一吗
Micah 2010年


3
用真正的Microsoft-Kool-Aid饮用方式说!
加里·钱伯斯

2

我知道这有点晚了,但我想补充一点,人们放弃了电子邮件帐户,服务提供商恢复了该地址,允许其他人使用它。

正如@HLGEM所指出的那样:“ Jsmith@somecompany.com可以很容易地在一年后归属于John Smith,而在两年后可以归属于Julia Smith。” 在这种情况下,如果约翰·史密斯(John Smith)需要您的服务,您要么拒绝使用他的电子邮件地址,要么删除所有与朱莉娅·史密斯(Julia Smith)有关的记录。

如果您必须删除记录,并且它们与取决于当地法律的企业财务历史相关,则您可能会陷入困境。

因此,我绝不会将电子邮件地址,车号牌等数据用作主键,因为无论它们看起来多么独特,它们都将超出您的控制范围,并且可能会带来一些您可能没有时间处理的有趣挑战。


2

您可能需要考虑任何适用的数据法规。电子邮件是个人信息,例如,如果您的用户是欧盟公民,那么在GDPR下,他们可以指示您从记录中删除其信息(请注意,无论您身在哪个国家/地区,该信息都适用。

如果出于引用完整性或历史原因(例如审核)需要将记录本身保留在数据库中,则使用代理键将使您可以将所有个人数据字段都设为NULL。如果他们的个人数据是主键,这显然不是那么容易


1

您可以使用整数主键来提高性能。


1

您应该使用整数主键。如果您需要电子邮件列是唯一的,为什么不简单在该列上设置唯一索引呢?


1

如果您有一个非int值作为主键,那么对大数据的插入和检索将非常慢。


1
不,插入它会比较,因为您需要两个唯一的索引:一个在生成的主键上,另一个在电子邮件地址上。
a_horse_with_no_name 2013年

1

主键应选择一个静态属性。由于电子邮件地址不是静态的,并且可以由多个候选者共享,因此将它们用作主键不是一个好主意。此外,电子邮件地址通常是一定长度的字符串,可能大于我们想要使用的唯一ID [len(email_address)> len(unique_id)],因此它将需要更多空间,甚至更糟的是,它们会多次存储为外键。因此,它将导致性能下降。



0

如果仅是要求电子邮件具有唯一性,那么您可以使用该列创建唯一性索引。


0

电子邮件是一个很好的唯一索引候选者,但是不是主键的候选者,如果它是主键,则您将无法更改联系人的电子邮件地址。我认为您的联接查询也将变慢。


0

不要使用电子邮件地址作为主键,保持电子邮件的唯一性,但不要将其用作主键,使用用户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.