支持表上的自然与代理键

时间:2012-10-05 14:30:06

标签: database-design primary-key surrogate-key natural-key

我已经阅读过许多关于自然与代理主键之间争斗的文章。 我同意使用代理键来识别其内容由用户创建的表的记录。

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

例如,在假设的表“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"}

现在让我们举一个例子:用户输入新订单。

在使用自然键的情况下,在代码中我可以这样写:

newOrder.StateOrderId = "NEW";

每次我有额外的步骤时,使用代理键。

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

newOrder.StateOrderId = stateOrderId_NEW;

每次我必须将订单移动到新状态时,都会发生同样的情况。

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

3 个答案:

答案 0 :(得分:2)

答案是:这取决于。

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

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

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

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

但是说了这么多,只需要仔细考虑。就个人而言,我觉得代理键已经变成了教条;许多开发人员将在他们的所有表中使用它们,并且建模软件将在创建表时自动添加它们。因此,你可能会对你的选择反应不一,但没有硬性规则禁止你使用它们;明智地选择:)

答案 1 :(得分:1)

简而言之:

  • 自然键可能会减少JOINing 1
  • 但还需要更多空间 2 (因此会影响缓存性能 3 )。

这里没有硬性规定。首先确定你是否需要这样的JOIN,如果你这样做,是否值得消除它是值得为增加存储的价格付出代价。唯一的方法是在实际数据量上衡量

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

  • 级联更新,
  • 聚类,
  • 菱形依赖等。

......但他们在很大程度上不适用于您的案件。


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

2 据推测,“主”表很大,在这种情况下,存储许多字符串(用于迁移的自然键)比存储许多字符(对于迁移的代理项)节省空间更少。如果主表很小,那么无论如何都无关紧要。

3 行“更胖”,因此较少的行将适合单个数据库页面。缓存通常在页面级别实现。

答案 2 :(得分:0)

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

显然,存储整数比存储varchar使用更少的磁盘空间,尤其是必须为最长的varchar分配空间(在您的情况下,“管理”)。我想用整数索引比用字符串索引要快(索引也会占用更少的空间)。

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

习惯于编写诸如

之类的查询
select orders.ordname
from orders
inner join orderstatus on orders.status = orderstatus.id
where orderstatus.name = 'NEW'

虽然老实说,我会使用一个标志字段来显示状态是否表示初始状态,“新”状态,而不是检查状态名称 - 状态仍然是初始状态,即使您更改它的名字。

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