何时使用一个字段作为主键而不是2?

时间:2012-12-20 02:21:00

标签: database database-design

我经常看到这样的数据库设计:

案例1:

用户表

- id [自动增加]

- 用户名

- 密码

- 电子邮件

案例2:

用户表

- 用户名

- 密码

- 电子邮件

RoleTable:

- 角色ID

- ROLENAME

UserTableRole:

- id [自动增加]

- 用户名

- 角色ID

我有以下问题:

案例1: 为什么不使用 UserName 字段作为主键( PK )?为什么使用其他字段喜欢 id [自动增加]作为PK? 如果只是用户名电子邮件,为什么不将电子邮件用作 PK ? 那么,最好的方法是什么?

案例2: 在UserRoleTable中,为什么不同时使用 UserName RoleID 作为PK?为什么要使用 id [自动增加]作为 PK ? 那么,在这种情况下,最好的方法是什么?

4 个答案:

答案 0 :(得分:3)

  

案例1:为什么不使用UserName字段作为主键(PK)?为什么使用另一个字段喜欢id [自动增加]作为PK?

UserTable.UserName在此数据模型中具有内在含义,称为"自然键" 。另一方面,UserTable.id"代理键"

如果你的模型中有一个自然键,你不能用代理键消除它,你可以取代它。所以问题是:你只使用自然键,或自然的代理键吗?这两种策略实际上都是有效的,并且有其优点和缺点。

代理密钥的典型原因:

  • 将FK保持在子表更细(在这种情况下为整数与字符串),以实现更小的存储和更好的缓存。
  • 避免使用ON UPDATE CASCADE。
  • 对ORM工具的友善。

另一方面:

  • 您现在有两个键而不是一个,需要额外的索引,使父表更大,缓存更少,并且由于索引维护而减慢INSERT / UPDATE // DELETE。 SUP> 1
  • 可能需要更多的加入 2
  • 可能不适合clustering 3
  

如果只是UserName和Email,为什么不使用Email作为PK?

如果用户更改了电子邮件,设计人员可能希望避免使用ON CASCADE UPDATE。

  

在案例2中:在UserRoleTable中,为什么不同时使用UserName和RoleID作为PK?

如果同一个用户/角色对没有多个连接,则无论如何都必须有一个密钥。

除非存在引用UserTableRole的FK或使用不友好的ORM的子表,否则没有理由使用额外的代理PK。


1 如果使用聚类,自然键下的二级索引可能是额外的" fat" (因为它包含群集密钥的副本,通常是PK)并且在查询时需要双重查找(因为群集表中的行不具有稳定的物理位置,因此必须通过群集密钥定位,禁止一些特定于DBMS的优化,例如Oracle" rowid猜测")。

2 例如只需阅读联结表,您就无法找到UserName - 您必须使用UserTable加入{{1}}。

3 代理通常以对客户端应用程序无意义的方式排序。自动增量代理键的顺序取决于INSERT的顺序,并且查询通常不是按照其插入顺序在用户范围内完成的。诸如GUID之类的一些代理可能会随机排序。

答案 1 :(得分:0)

我可以想到的一个原因是,不使用像UserName这样的东西作为主键,它们可能会发生变化。将任何暴露于外部世界的东西作为主键都会冒这些事情发生变化的风险,并且最好有一个稳定的主键。

如果用户更改了电子邮件或用户名,该怎么办?你真的想在你所有的关系中改变你的钥匙吗? IMO,最好有一个永远不会看到外部世界的稳定密钥,每个人都不知道,因此无论数据库中可能发生什么变化,都可以保持稳定。

答案 2 :(得分:0)

您的问题基本上是使用natural vs surrogate key的优点和缺点。

灵活性是首要考虑因素,代理键是您可以更轻松地更改其用户名。并且将来可能需要允许重复的用户名,例如兼并。

速度是另一个问题,在频繁访问的表(如用户表)上,对整数的连接比在字符串上进行连接通常更快。

另一种是表大小,当用作外键时,您必须存储整个键的值。代理非常紧凑,比自然键小得多。

大多数ORM还需要使用代理,因为它提供了表之间的一致性。

此外,在许多系统上,假设电子邮件是唯一的,可能不一定安全。

我同意在UserRole这样的关系表中,通常最好使用外键中的主复合键。

答案 3 :(得分:0)

我可以在您的示例中考虑使用代理主键(Id)而非用户名的几个原因。

  1. 如果有的话,id字段很少会更新。如果username是主键,则必须将更新级联到所有使用username作为外键的表。
  2. 性能。 int比较胜过字符串比较。
  3. id密钥会占用更少的存储空间,而在其他表中它是外键。
  4. id字段允许您不公开敏感数据。例如。考虑一个web app url domain / posts / user / 1242 vs domain / posts / user / myusername
  5. 对于您的第二个问题,最好使用userid而不是UserTableRole中的用户名。当时是否更好还包括这个多对多表的代理键是一个意见问题。我讨厌在许多表中使用代理id键,并且通常只是制作两个外键ID的复合主键。我在这里考虑代理键的唯一一次是,如果我需要在另一个表中使用它作为外键。