代码之家  ›  专栏  ›  技术社区  ›  Manrico Corazzi

代理密钥与自然/业务密钥[已关闭]

  •  157
  • Manrico Corazzi  · 技术社区  · 16 年前

    我们再来一次,老的争论仍然出现…

    我们最好使用一个业务密钥作为主键,还是使用一个对业务密钥字段具有唯一约束的代理ID(即SQL Server标识)?

    请提供例子或证据来支持你的理论。

    19 回复  |  直到 7 年前
        1
  •  88
  •   Ted    16 年前

    两者都有。吃你的蛋糕吧。

    记住,主键没有什么特别的地方,只是它被标记为主键。它只是一个不为空的唯一约束,一个表可以有多个唯一约束。

    如果您使用代理密钥,您仍然需要一个业务密钥来确保根据业务规则的唯一性。

        2
  •  108
  •   Jay Shepherd    7 年前

    使用代理键的几个原因:

    1. 稳定性 :由于业务或自然需要而更改密钥将对相关表产生负面影响。代理键很少,如果有的话,需要改变,因为没有意义与价值挂钩。

    2. 公约 :允许您使用标准化的主键列命名约定,而不必考虑如何将具有不同名称的表连接到它们的pk。

    3. 速度 :根据pk值和类型,整数的代理键可能更小,索引和搜索速度更快。

        3
  •  65
  •   Community CDub    8 年前

    似乎还没有人说过任何支持非代理(我犹豫说“自然”)密钥的话。所以这里…

    缺点 代理键的作用是 无意义的 (一些人认为这是一种优势,但…)。这有时会迫使您在查询中加入比实际需要的更多的表。比较:

    select sum(t.hours)
    from timesheets t
    where t.dept_code = 'HR'
    and t.status = 'VALID'
    and t.project_code = 'MYPROJECT'
    and t.task = 'BUILD';
    

    反对:

    select sum(t.hours)
    from timesheets t
         join departents d on d.dept_id = t.dept_id
         join timesheet_statuses s on s.status_id = t.status_id
         join projects p on p.project_id = t.project_id
         join tasks k on k.task_id = t.task_id
    where d.dept_code = 'HR'
    and s.status = 'VALID'
    and p.project_code = 'MYPROJECT'
    and k.task_code = 'BUILD';
    

    除非有人认真地认为以下是个好主意?:

    select sum(t.hours)
    from timesheets t
    where t.dept_id = 34394
    and t.status_id = 89    
    and t.project_id = 1253
    and t.task_id = 77;
    

    但是“有人会说,”当myproject或valid或hr的代码更改时会发生什么?我的回答是:“你为什么要 需要 要改变它吗?”从某种意义上说,这些不是“自然”的钥匙,因为一些外部机构将立法规定,今后“有效”应重新编码为“良好”。只有一小部分“自然”密钥真正属于这一类别——SSN和邮政编码是常见的例子。我肯定会对person、address之类的表使用一个无意义的数字键,但不用于 一切 因为某些原因,这里的大多数人似乎都主张。

    参见: my answer to another question

        4
  •  29
  •   tzot    16 年前

    代理键(通常是整数)具有使表关系更快、存储更经济和更新速度更快的附加值(更好的是,在使用代理键时不需要更新外键,而业务键字段则会不时发生更改)。

    表的主键应用于唯一标识行,主要用于联接目的。想想一个人的桌子:名字可以改变,而且不能保证它们是唯一的。

    思考公司:你是一个快乐的默金公司与其他公司在默基亚做生意。您足够聪明,不必使用公司名称作为主键,所以您可以使用Merkia政府的唯一公司ID,该ID由10个字母数字字符组成。 然后,Merkia改变了公司ID,因为他们认为这是个好主意。没关系,你可以使用你的数据库引擎的级联更新功能,进行一个不应该首先涉及到你的变更。后来,你的生意扩大了,现在你在弗里多尼亚的一家公司工作。Freedonian公司ID最多16个字符。您需要扩大公司ID主键(也包括订单、问题、货币传递等中的外键字段),在主键中添加国家/地区字段(也包括外键)。哎哟!自由多尼亚内战,在三个国家分裂。您同事的国家/地区名称应更改为新名称;救援的级联更新。顺便问一下,你的钥匙是什么?(国家,公司ID)还是(公司ID,国家)?后者有助于联接,前者避免使用另一个索引(如果您希望订单也按国家分组,则可能是许多索引)。

    所有这些都不是证据,但表示为所有用途(包括联接操作)唯一标识行的代理键比业务键更可取。

        5
  •  28
  •   Danijel Arsenovski    16 年前

    代理键永远不会有更改的理由。我不能对自然键说同样的话。姓氏、电子邮件、国际标准图书编号公告——它们都可以在一天内更改。

        6
  •  26
  •   shA.t Rami Jamleh    9 年前

    我讨厌一般的代孕钥匙。只有在没有优质的自然钥匙可用时才应使用它们。当你想到这一点时,认为向你的表中添加无意义的数据可以使事情变得更好是相当荒谬的。

    以下是我的理由:

    1. 使用自然键时,表以最常被搜索的方式进行集群,从而加快查询速度。

    2. 使用代理键时,必须在逻辑键列上添加唯一索引。您仍然需要防止逻辑重复数据。例如,即使pk是一个代理ID列,也不能允许组织表中有两个同名的组织。

    3. 当代理键用作主键时,自然主键是什么就不太清楚了。在开发时,您希望知道哪些列集使表具有唯一性。

    4. 在一对多关系链中,逻辑键链。例如,组织有许多帐户,帐户有许多发票。所以组织的逻辑键是orgname。帐户的逻辑键是OrgName、AccountID。发票的逻辑键是OrgName、AccountID、InvoiceNumber。

      当使用代理键时,键链只被直接父级的外键截断。例如,发票表没有OrgName列。它只有一列用于accountID。如果要搜索给定组织的发票,则需要加入组织、帐户和发票表。如果使用逻辑键,则可以直接查询组织表。

    5. 存储查找表的代理键值会导致表中填充无意义的整数。要查看数据,必须创建连接到所有查阅表格的复杂视图。查阅表格用于保存列的一组可接受值。它不应该通过存储整数代理键来编码。规范化规则中没有建议您应该存储一个代理整数而不是值本身的内容。

    6. 我有三本不同的数据库书。其中没有一个显示使用代理键。

        7
  •  16
  •   Community CDub    8 年前

    我想和你们分享我在这场无休止的战争中的经验:关于自然与替代钥匙困境。我认为 二者都 代理键(人工自动生成的)和自然键(由具有域含义的列组成)具有 赞成的意见 欺骗 . 因此,根据您的情况,选择一种方法或另一种方法可能更为相关。

    似乎很多人都把代孕键作为近乎完美的解决方案,把自然键作为瘟疫,我将着重讨论另一个观点的论点:

    代理键的缺点

    代理键是:

    1. 性能问题来源:
      • 它们通常使用自动递增的列来实现,这意味着:
        • 每次您想要获得一个新的ID时,都要来回访问数据库(我知道可以使用缓存或类似于[seq]hilo的算法来改进这一点,但这些方法仍然有其自身的缺点)。
        • 如果有一天您需要将数据从一个模式移动到另一个模式(至少在我的公司经常发生这种情况),那么您可能会遇到ID冲突问题。是的,我知道你可以使用UUID,但是那些持续的需要32个十六进制数字!(如果您关心数据库大小,那么这可能是一个问题)。
        • 如果您对所有的代理键使用一个序列,那么—当然—您将以数据库上的争用结束。
    2. 容易出错。序列具有最大值限制,因此作为开发人员,您必须注意以下几点:
      • 您必须循环您的序列(当达到最大值时,它返回到1,2,…)。
      • 如果使用序列作为数据的顺序(随着时间的推移),则必须处理循环的情况(ID为1的列可能比ID最大值为-1的行更新)。
      • 确保您的代码(甚至您的客户机接口,它们不应该像内部ID那样发生)支持用于存储序列值的32b/64b整数。
    3. 它们不保证不重复的数据。始终可以有两行具有相同的列值,但生成的值不同。对我来说这就是 这个 从数据库设计的角度来看,代理键的问题。
    4. More in Wikipedia...

    自然钥匙的神话

    1. 复合键比代理键效率低。不!它取决于使用的数据库引擎:
    2. 自然钥匙在现实生活中并不存在。抱歉,它们确实存在!例如,在航空工业中,对于给定的 计划的 航班(航空公司、离港日期、航班号、操作系统)。更一般地说,当一组业务数据通过给定的 标准 那么,这组数据是一个[好的]自然关键候选。
    3. 自然键“污染子表的模式”。对我来说,这更多的是一种感觉,而不是一个真正的问题。拥有4列的主键(每列2个字节)可能比一列11个字节更有效。此外,这4列可用于直接查询子表(使用WHERE子句中的4列),而无需联接到父表。

    结论

    在与此相关的情况下使用自然键,在更好的情况下使用代理键。

    希望这对某人有帮助!

        8
  •  14
  •   Iain Holder    16 年前

    总是使用一把没有商业意义的钥匙。这只是个很好的练习。

    编辑:我想在网上找到一个链接,但是我找不到。但是在 'Patterns of Enterprise Archtecture' [福勒]它很好地解释了为什么你不应该使用钥匙以外的任何东西,除了作为一把钥匙没有任何意义。归根结底,它应该只有一个工作和一个工作。

        9
  •  9
  •   derek lawless    16 年前

    如果您计划使用ORM工具来处理/生成数据类,那么代理键非常方便。虽然您可以将复合键与一些更高级的映射器一起使用(阅读:hibernate),但它会给您的代码增加一些复杂性。

    (当然,数据库纯粹主义者会争辩说,即使是代理密钥的概念也是可憎的。)

    我很喜欢在合适的时候使用uid作为代理键。它们的主要优势在于您提前知道密钥,例如,您可以创建一个ID已设置并保证为唯一的类实例,而使用整数密钥,则在保存/更新时需要默认为0或-1并更新到适当的值。

    uid在查找和连接速度方面会受到惩罚,因此它取决于所讨论的应用程序是否需要它们。

        10
  •  6
  •   Mark Embling    16 年前

    在我看来,使用代理键更好,因为它变化的可能性为零。几乎任何我能想到的,你可以用它作为自然钥匙的东西都可能改变(免责声明:不总是正确的,但通常是这样)。

    一个例子可能是汽车的数据库-乍一看,你可能认为牌照可以用作钥匙。但这些可以改变,所以这是个坏主意。你不会真的想知道的 之后 发布这个应用程序,当有人来找你,想知道为什么他们不能把他们的车牌换成他们崭新的个性化车牌。

        11
  •  5
  •   user7658    16 年前

    如果可能,请始终使用单列代理键。这使得连接以及插入/更新/删除更加清晰,因为您只负责跟踪单个信息以维护记录。

    然后,根据需要,将业务密钥作为唯一的约束或索引进行堆叠。这将保持数据完整性。

    业务逻辑/自然键可以更改,但表的物理键不应更改。

        12
  •  4
  •   Santiago Cepas    13 年前

    在数据仓库场景中,我认为最好遵循代理键路径。原因有二:

    • 您独立于源系统,那里的更改(如数据类型更改)不会影响您。
    • 您的DW将需要更少的物理空间,因为您将只使用整数数据类型作为代理键。而且,您的索引也会工作得更好。
        13
  •  2
  •   David Thornley    16 年前

    当业务信息可以更改或相同时,代理键可能很有用。毕竟,企业名称在全国范围内不必是唯一的。假设你和两个叫史密斯电子公司的公司打交道,一个在堪萨斯州,一个在密歇根州。你可以按地址区分,但会改变的。即使是州政府也可以改变;如果堪萨斯州堪萨斯城的史密斯电子公司横渡大河来到密苏里州堪萨斯城呢?没有明显的方法可以通过自然的密钥信息来保持这些业务的独特性,因此代理密钥非常有用。

    把代理键想象成一个ISBN号。通常,你通过书名和作者来识别一本书。然而,我有两本H.P.Willmott写的《珍珠港》,它们绝对是不同的书,不仅仅是不同的版本。在这样的情况下,我可以参考书籍的外观,或者先是后,但我也可以参考ISBN。

        14
  •  2
  •   Bryan    12 年前

    作为提醒,在随机代理键(即读取xy8d7-dfd8s的guid)上放置聚集索引是不好的做法,因为它们SQL Server无法对这些数据进行物理排序。您应该在这些数据上放置唯一的索引,尽管对于主表操作简单地运行SQL事件探查器,然后将这些数据放到数据库引擎优化顾问中可能也很有好处。

    见线@ http://social.msdn.microsoft.com/Forums/en-us/sqlgetstarted/thread/27bd9c77-ec31-44f1-ab7f-bd2cb13129be

        15
  •  2
  •   Stefanos Kargas    12 年前

    案例1: 你的桌子是 查找表 少于50种类型(插入件)

    使用 商务/自然钥匙 . 例如:

    Table: JOB with 50 inserts
    CODE (primary key)       NAME               DESCRIPTION
    PRG                      PROGRAMMER         A programmer is writing code
    MNG                      MANAGER            A manager is doing whatever
    CLN                      CLEANER            A cleaner cleans
    ...............
    joined with
    Table: PEOPLE with 100000 inserts
    
    foreign key JOBCODE in table PEOPLE
    looks at
    primary key CODE in table JOB
    

    案例2: 你的桌子是 带有数千个插入的表格

    使用 代理键/自动增量键 . 例如:

    Table: ASSIGNMENT with 1000000 inserts
    joined with
    Table: PEOPLE with 100000 inserts
    
    foreign key PEOPLEID in table ASSIGNMENT
    looks at
    primary key ID in table PEOPLE (autoincrement)
    

    在第一种情况下:

    • 您可以选择表中的所有程序员而不使用join with table job,但只需使用:“select*from people where jobcode='prg'”

    在第二种情况下:

    • 数据库查询更快,因为主键是整数
    • 您不必费心去寻找下一个唯一的键,因为数据库本身提供了下一个自动增量。
        16
  •  2
  •   Michael Green    8 年前

    在这种情况下,代理键 总是 有道理。在某些情况下,您要么选择最适合数据库的内容,要么选择最适合对象模型的内容,但在这两种情况下,最好使用无意义的密钥或guid。它使索引更容易、更快,而且它是对象的标识,不会发生更改。

        17
  •  1
  •   WillC    13 年前

    马为课程。为了说明我的偏见,我首先是一个开发人员,所以我主要关心的是为用户提供一个可工作的应用程序。

    我已经在使用自然键的系统上工作过,并且必须花费大量的时间来确保价值的变化会波及整个系统。

    我研究过只使用代理键的系统,唯一的缺点是缺乏用于分区的非规范化数据。

    我曾与之合作过的大多数传统的PL/SQL开发人员都不喜欢代理键,因为每个联接都有表的数量,但是我们的测试和生产数据库从来都不会增加负担;额外的联接不会影响应用程序的性能。对于不支持“x.a=y.b上的x inner join y”这样的子句的数据库方言,或者不使用该语法的开发人员,代理键的额外联接确实会使查询更难读取,而且键入和检查的时间更长:请参阅@t on y andrews post。但是,如果您使用ORM或任何其他SQL生成框架,您将不会注意到它。触摸式打字也可以减轻。

        18
  •  1
  •   lrb    12 年前

    也许与这个话题不完全相关,但我在处理代理键时有点头疼。Oracle预先交付的分析在仓库中的所有维度表上创建自动生成的sk,并将这些sk存储在事实上。因此,每当需要在添加新列时重新加载它们(维度),或者需要为维度中的所有项填充它们时,在更新期间分配的SKS会使SKS与存储到事实的原始值不同步,从而强制完全重新加载所有与之相连的事实数据表。我希望即使sk是一个无意义的数字,也有一些方法不能改变原始/旧记录。正如许多人所知,开箱即用很少能满足组织的需求,我们必须不断地进行定制。现在我们的仓库中有价值3年的数据,从Oracle金融系统中完全重新加载的数据量非常大。所以在我的例子中,它们不是从数据输入中生成的,而是添加到仓库中以帮助报告性能。我明白了,但我们确实改变了,这是一场噩梦。

        19
  •  0
  •   kanad    16 年前

    在时间点数据库的情况下,最好使用代理键和自然键的组合。例如,您需要跟踪俱乐部的会员信息。成员的某些属性永远不会更改。例如出生日期,但姓名可以更改。 因此,创建一个成员表,其中包含一个member_id代理键,并有一个用于dob的列。 创建另一个名为person name的表,并为member_id、member_fname、member_lname、date_updated列。在此表中,自然键将是成员_id+日期_updated。