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

cna*_*reu 47 sql database database-design data-modeling

我正在学习一门名为"数据库系统"的课程,对于我们的课程项目,我必须设计一个网站.

这是我创建的表的示例:

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

教授告诉我"uid"(用户ID)完全无用且不必要,我应该使用用户名作为主键,因为没有两个用户可以拥有相同的用户名.

我告诉他我使用用户ID很方便,因为当我调用domain.com/viewuser?id=5之类的东西时,我只需检查参数:is_numeric($_GET['id'])......不用说他不相信.

由于我在大量教程中看到了user_id和其他类似属性(thread_id,comment_id等)并且查看了流行软件(例如vbulletin)的数据库模式,因此必须有很多其他(更强大的)原因.

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

mea*_*gar 82

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

  • 它们允许在Stack Overflow上使用重复的用户名
  • 它们允许更改用户名(或电子邮件地址,如果用于登录)(轻松)
  • 选择,连接和插入比varchar主键更快,因为它更快地维护数字索引
  • 正如您所提到的,验证变得非常简单: if ((int)$id > 0) { ... }
  • 输入的卫生是微不足道的: $id = (int)$_GET['id']
  • 由于外键不必复制可能较大的字符串值,因此开销要少得多

我想说当一个自动递增的数字键很容易获得时,尝试使用任何一个字符串信息作为记录的唯一标识符是一个坏主意.

具有唯一用户名的系统适用于极少数用户,但是互联网已经使它们从根本上被打破.当您考虑可能需要与网站进行交互的名为"john"的人数时,要求每个人使用唯一的显示名称是荒谬的.它导致了我们经常看到的可怕系统,随机数字和字母装饰用户名.

但是,即使在您强制使用唯一用户名的系统中,它仍然是主键的不良选择.想象一下有500个帖子的用户:posts表中的外键将包含用户名,重复500次.即使在您考虑有人可能最终需要更改其用户名之前,开销也是令人望而却步的.

  • 告诉'教授'阅读这个答案;) (26认同)
  • 另一点 - ORM讨厌非整数键.虽然我觉得这是大多数ORM的一般失败,但你确实学会用*工具* (8认同)
  • 非独特的显示用户名促进社会工程 - 我不会称之为如此伟大的想法. (4认同)

Mat*_*ell 15

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

  • 如果用户可以改变他/她/他的用户名,那么根据定义,用户名不是候选主键. (8认同)
  • 参考动作"ON UPDATE CASCADE"是这个"稻草人"论证的通常答案. (6认同)
  • @Bob Jarvis:一个好的关键属性,它的值是稳定的.不可改变是理想但罕见的变化也只是好的. (4认同)
  • @onedaywhen:如果你使用`ON UPDATE CASCADE`,而不是在一个地方更改用户名,你可能会在潜在的大量索引字段中更改它.意味着巨大的性能打击.当然,您需要在事务中执行此操作,以免有人看到链接到不再存在的用户名的记录.在许多DB中,这意味着锁定相关的行.当然,用户不应经常更改其用户名.但是,如果两个人同时这样做,并且他们共享任何共同的记录(例如,站内消息),那么您正在寻找各地的潜在问题. (2认同)

one*_*hen 11

如果你向你的教授证明,为每个用户分配一个唯一的任意整数对你的应用程序是有价值的,那么当然他说"完全无用且没必要"是错误的.

不过,也许你错过了他的观点.如果他告诉你要求是"没有两个用户可以拥有相同的用户名"那么你就没有达到这个要求.

真诚地感谢发布您的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);
Run Code Online (Sandbox Code Playgroud)

结果如下:

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>
Run Code Online (Sandbox Code Playgroud)

我认为这是你的教授试图做的一点:如果没有强制执行自然键,username你根本就没有任何数据完整性.

如果我是教授,我也会敦促你从你的设计中删除可以为空的列.


RC.*_*RC. 7

这通常被称为代理键,它有许多好处.其中之一是使数据库关系与应用程序数据隔离.更多细节和相应的缺点可以在上面提供的wiki链接中找到.