为什么在其他唯一字段存在时使用自动递增主键?

我正在学习一门叫做“数据库系统”的课程,对于我们的课程项目,我必须devise一个网站。

这是我创build的一个表格的例子:

CREATE TABLE users ( uid INT NOT NULL AUTO_INCREMENT, username VARCHAR(60), passhash VARCHAR(255), email VARCHAR(60), rdate DATE, PRIMARY KEY(uid) ); 

教授告诉我“用户名”(用户名)是完全无用的和不必要的,我应该使用用户名作为主键,因为没有两个用户可以有相同的用户名。

我告诉他,这对我来说很方便,因为当我打电话给domain.com/viewuser?id=5时,我只需要检查参数: is_numeric($_GET['id']) …不用说他不相信。

由于我在很多教程中看到了user_id和其他类似的属性(thread_id,comment_id等等),并且正在查看stream行软件(例如vbulletin)的数据库模式,所以还有很多其他的(更强的)原因。

所以我的问题是:你如何certificate需要一个非空自动递增的id作为主键vs使用另一个属性,如用户名?

自动递增主键有以下几个原因:

  • 他们允许重复的用户名称堆栈溢出
  • 他们允许用户名(或电子邮件地址,如果这是用来login)被改变(容易)
  • select,连接和插入比varchar主键更快,因为它更快地维护一个数字索引
  • 如你所说,validation变得非常简单: if ((int)$id > 0) { ... }
  • input的卫生是微不足道的: $id = (int)$_GET['id']
  • 由于外键不必重复可能的大string值,因此开销要小得多

我想说,当一个自动递增的数字键是如此容易可用时,试图使用任何一条string信息作为logging的唯一标识符是一个坏主意。

具有独特用户名称的系统对于非常less量的用户来说是好事,但是互联网已经使他们从根本上被打破了。 当你考虑名为“john”的人数可能不得不与一个网站进行交互时,要求他们每个人都使用一个唯一的显示名称是很荒谬的。 这导致了我们经常看到的可怕的系统,随机的数字和字母装饰一个用户名。

但是,即使在您执行唯一用户名的系统中,对主键来说仍然是一个糟糕的select。 想像一下有500个post的用户: posts表中的外键将包含用户名,重复500次。 甚至在你认为有人可能最终需要改变他们的用户名之前,开销是令人望而生畏的。

如果用户名是主键,并且用户更改了他/她的用户名,则需要更新对users表具有外键引用的所有表。

如果你已经向你的教授certificate,为每个用户分配一个唯一的任意整数对你的应用程序是有价值的,那么当然他会错误的说它是“完全无用和不必要的”。

但是,也许你错过了他的观点。 如果他告诉你,要求是“没有两个用户可以有相同的用户名”,那么你还没有达到这个要求。

真诚地感谢您发布您的SQL DDL,这是非常有用的,但大多数不打扰SO。

用你的桌子,我可以做到这一点:

 INSERT INTO users (username) VALUES (NULL); INSERT INTO users (username) VALUES (NULL); INSERT INTO users (username) VALUES (NULL); INSERT INTO users (username) VALUES (NULL); INSERT INTO users (username) VALUES (NULL); 

结果如下:

 SELECT uid, username, passhash, email, rdate FROM users; uid username passhash email rdate 1 <NULL> <NULL> <NULL> <NULL> 2 <NULL> <NULL> <NULL> <NULL> 3 <NULL> <NULL> <NULL> <NULL> 4 <NULL> <NULL> <NULL> <NULL> 

我认为你的教授试图提出的观点是:如果不强制username的天然秘密,你根本就没有任何数据完整性。

如果我是教授,我也会敦促你从你的devise中删除空列。

这通常被称为代理键 ,它有很多好处。 其中之一就是将数据库关系与应用程序数据隔离开来。 更多细节和相应的缺点可以在上面提供的维基链接中find。

因为有人可能想要更改他们的用户名(或任何名称)。

你的教授正在做正确的事情,指出你应该使用户名是唯一的,如果用户名是唯一的,则不能为空。 uid也可以是一个关键,但除非你真的在某个地方使用它,否则不需要。 devise的更重要的方面应该是实现自然的关键。 所以我同意你的教授的评论。

我需要一个拥有更多数据库知识的人来支持我,但是我相信你在外键查询的时候会得到更快的响应。

此外,您可能稍后决定要更改用户名,或者对用户名的要求可能会更改(可能是更长的string?)。 使用ID可防止更改所有外键。

让我们面对现实吧,大多数项目都不会扩展那么多,但是如果现在能够符合良好的编程标准,您是否真的想冒着头痛12个月的风险呢?

例如,整数search(?id = 5)比stringsearch(?username = bob)快得多,基数更高。 另一个例子,uid是auto_increment,所以你不必显式地插入它,但是它会在每个插入查询中自动递增。

PS:你的教授真的错了:D

我们使用ID来防止重复数据,它可以使一些过程变得不复杂(如果我们想更新或删除数据),如果我们使用ID更简单。

如果你不想使用ID,你可以使用其他字段。 但不要忘记让它们成为唯一。 它可以使您的数据防止重复数据。

PRIMARY之外的另一种方式是独特的。

我拿着上面所有的答案。 我会说一个ID很容易实现,当涉及到索引时,INT总是首选与varchar比较。 你的教授应该知道些什么,他为什么会说Int#是在我之上!

因为userid应该是唯一的(不能被复制),有时也是索引。

你想要明文存储你的用户名为任何一个偷? 我永远不会考虑使用我可能想要encryption的自然密钥(或者现在要encryption)。