【问题标题】:Natural vs surrogate keys on support tables [closed]支持表上的自然键与代理键[关闭]
【发布时间】:2012-09-26 17:47:00
【问题描述】:

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

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

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

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

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

【问题讨论】:

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


    【解决方案1】:

    答案是:视情况而定。

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

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

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

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

    不过说了这么多,还是要慎重考虑。我个人觉得代理键已经成为一种教条。许多开发人员会在他们的所有表格中使用它们,建模软件会在创建表格时自动添加它们。因此,您可能会对您的选择产生不同的反应,但没有硬性规定禁止您使用它们;明智地选择:)

    【讨论】:

    • "在您更改代码中的订单状态的示例中,问问自己为这些状态创建常量的可能性有多大(例如,为了避免拼写错误)。如果是这样,两者都会完成相同。”好点子。现在我将枚举与键的值一起使用。
    【解决方案2】:

    简而言之:

    • 自然键可能导致更少的JOINing1,
    • 但也需要更多空间2(因此会损害缓存性能3)。

    这里没有硬性规定。首先确定您是否需要这样的 JOIN,如果需要,是否值得为增加存储而付出代价。做到这一点的唯一方法是衡量真实的数据量。

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

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

    ...但它们在大多数情况下不适用于您的情况。


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

    2 假设“主”表很大,在这种情况下,存储许多字符串(对于迁移的自然键)比存储许多整数(对于迁移的代理)的空间效率低.如果主表很小,那么无论哪种方式都无关紧要。

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

    【讨论】:

      【解决方案3】:

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

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

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

      习惯上写查询如

      select orders.ordname
      from orders
      inner join orderstatus on orders.status = orderstatus.id
      where orderstatus.name = 'NEW'
      

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

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

      【讨论】:

      • 我了解,但在这种情况下,用户无法编辑 OrderStatus 的值。所以不存在重复的问题。
      猜你喜欢
      • 2013-12-01
      • 2011-12-19
      • 2011-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多