Database design 支持表上的自然键与代理键

Database design 支持表上的自然键与代理键,database-design,primary-key,surrogate-key,natural-key,Database Design,Primary Key,Surrogate Key,Natural Key,我读过很多关于自然主键和代理主键之争的文章。 我同意使用代理键来标识其内容由用户创建的表的记录 但是在支持表的情况下,我应该使用什么? 例如,在一个假设的表“orderStates”中。 此表中的值不可编辑(用户无法插入、修改或删除此值) 如果使用自然密钥,则将具有以下数据: TABLE ORDERSTATES {ID: "NEW", NAME: "New"} {ID: "MANAGEMENT" NAME: "Management"} {ID: "SHIPPED" NAME: "Shipped"

我读过很多关于自然主键和代理主键之争的文章。 我同意使用代理键来标识其内容由用户创建的表的记录

但是在支持表的情况下,我应该使用什么?

例如,在一个假设的表“orderStates”中。 此表中的值不可编辑(用户无法插入、修改或删除此值)

如果使用自然密钥,则将具有以下数据:

TABLE ORDERSTATES
{ID: "NEW", NAME: "New"}
{ID: "MANAGEMENT" NAME: "Management"}
{ID: "SHIPPED" NAME: "Shipped"}
TABLE ORDERSTATES
{ID: 1 CODE: "NEW", NAME: "New"}
{ID: 2 CODE: "MANAGEMENT" NAME: "Management"}
{ID: 3 CODE: "SHIPPED" NAME: "Shipped"}
如果使用代理密钥,则将具有以下数据:

TABLE ORDERSTATES
{ID: "NEW", NAME: "New"}
{ID: "MANAGEMENT" NAME: "Management"}
{ID: "SHIPPED" NAME: "Shipped"}
TABLE ORDERSTATES
{ID: 1 CODE: "NEW", NAME: "New"}
{ID: 2 CODE: "MANAGEMENT" NAME: "Management"}
{ID: 3 CODE: "SHIPPED" NAME: "Shipped"}
现在让我们举一个例子:用户输入一个新订单

在使用自然键的情况下,我可以在代码中编写以下内容:

newOrder.StateOrderId = "NEW";
每次我有一个额外的步骤时,使用代理键

stateOrderId_NEW = .... I retrieve the id corresponding to the recod code "NEW"

newOrder.StateOrderId = stateOrderId_NEW;
每次我必须将订单移动到新状态时,都会发生同样的情况


那么,在这种情况下,选择一种键类型而选择另一种键类型的原因是什么?

如果我理解正确,您的第一个示例显示表的主键是字符串(varchar),而在第二个示例中,主键是整数。主键可能是另一个表中的外键

显然,存储整数比存储varchar占用更少的磁盘空间,尤其是在必须为最长的varchar(在您的例子中是“management”)分配空间的情况下。我认为整数索引比字符串索引更快(索引占用的空间也更小)

第一个示例的主键和“name”字段具有相同的值;虽然更改名称不会更改主键(因此不会对使用“OrderState”作为外键的表产生影响),但会出现逻辑断开-您可以将主键“name”作为主键,而将值“Person”作为主键

通常编写查询,如

select orders.ordname
from orders
inner join orderstatus on orders.status = orderstatus.id
where orderstatus.name = 'NEW'
尽管说实话,我还是会使用一个标志字段来显示状态是否表示初始“新建”状态,而不是检查状态的名称——即使更改了名称,状态仍然是初始状态


您可以使用生成器提供保证唯一的密钥,而如果使用“自然”密钥,则必须检查碰撞。

答案是:视情况而定


在代码中更改顺序状态的示例中,问问自己为这些状态创建常量的可能性有多大(例如,为了避免拼写错误)。如果是这样的话,两者将实现相同的目标

在通过表单提交新订单状态的情况下,您可以使用自然键或代理键构建可能值的下拉列表(例如),没有区别

当您在订单表上执行查询并希望打印每个订单的状态时,情况会有所不同。拥有一个自然键可以避免进行另一个连接,这会有所帮助(尽管有点)

就存储和查询性能而言,在大多数情况下,代理键分别更小和更快(取决于表大小)

但话虽如此,它只是需要仔细考虑。就我个人而言,我觉得代用钥匙已经成为一种教条;许多开发人员将在所有表中使用它们,建模软件将在创建表时自动添加它们。因此,你可能会对你的选择有不同的反应,但没有硬性规定禁止你使用它们;明智地选择:)

简而言之:

  • 自然键可能会减少接合1
  • 但也需要更多的空间2(因此会影响缓存性能3)
这里没有硬性规定。首先确定您是否需要这样的连接,如果需要,消除它是否值得为增加存储而付出代价。做到这一点的唯一方法是对实际数据量进行测量

顺便说一句,在自然与代理的争论中还有其他考虑因素,例如

  • 层叠更新
  • 集群
  • 菱形的附属物等
……但它们在很大程度上不适用于你的情况


1个自然键将通过FK迁移到“main”表中,因此如果需要将其与主表行组合在一起,可以完全避免连接。顺便说一句,如果你需要一个不同的连接(为了获得一个非键),你将无法用这种方式消除它

2“main”表可能很大,在这种情况下,存储许多字符串(对于迁移的自然键)比存储许多int(对于迁移的代理项)节省空间。如果主表很小,那么无论哪种方式,它几乎都不重要


3行“更胖”,因此单个数据库页面中容纳的行更少。缓存通常是在页面级别实现的。

“在您更改代码中的顺序状态的示例中,问问自己,您为这些状态创建常量的可能性有多大(例如,为了避免拼写错误)。如果是这样,两者都可以完成相同的任务。”这一点很好。现在我正在使用带有键值的enum。我理解,但在这种情况下,用户不能编辑OrderStatus的值。因此,不存在重复的问题。