代码之家  ›  专栏  ›  技术社区  ›  Nate CSS Guy

数据库“key/id”设计思想、代理键、主键等

  •  5
  • Nate CSS Guy  · 技术社区  · 15 年前

    所以我最近看到过几次提到代理键,我不太确定它是什么,以及它与主键的区别。

    我总是假设ID是这样一个表中的主键:

    Users
        ID, Guid
        FirstName, Text
        LastName, Text
        SSN, Int
    

    然而,维基百科将代理键定义为“数据库中的代理键是模型世界中实体或数据库中对象的唯一标识符”。 代理项不是从应用程序数据派生的 ."

    根据维基百科,它看起来像是我的代理密钥,而我的主密钥可能是ssn+id?这是对的吗?那是个糟糕的桌子设计吗?

    假设表的设计是合理的,对于数据没有任何独特之处的表来说,这样做会不会很糟糕?

    LogEntry
        ID, Guid
        LogEntryID, Int [sql identity field +1 every time]
        LogType, Int
        Message, Text
    
    7 回复  |  直到 14 年前
        1
  •  12
  •   marc_s    15 年前

    不,您的ID可以同时是一个代理密钥(这意味着它不是“从应用程序数据派生的”,例如一个人工密钥)。 它也应该是您的主密钥。

    主键用于唯一、安全地标识表中的任何行。它必须是稳定的、唯一的,并且不能为空——一个“人工”ID通常具有这些属性。

    我通常建议不要对主键使用“自然”或实际数据-不是 真正地 150% 当然是 从未 要改变吗??例如,瑞士的SSN相当于女性每次结婚(或离婚)时都会发生变化——这几乎不是一个理想的候选人。它也不能保证是独一无二的……

    为了让自己省去所有的痛苦,只需要使用一个代理(人工)ID,它是系统定义的、唯一的,并且永远不会改变,也永远不会有任何应用意义(除了作为您的唯一ID之外)。

    ScottAmbler在这里有一篇很好的文章,它包含了所有不同的键及其含义的“词汇表”——你会发现自然键、代理键、主键等等。

        2
  •  6
  •   Charles Bretana    14 年前

    首先,代理键是在数据库中人为生成的键,作为表中每一行的唯一值,它不依赖于表中的任何其他属性。

    现在,这个短语 主键 是一条红鲱鱼。密钥是主密钥还是备用密钥并不意味着什么。关键是钥匙的用途。键可以提供两个基本上彼此不一致的功能。

    1. 他们是 首先 确保数据的完整性和一致性!表中的每一行表示表中定义用于保存数据的任何实体的实例。不 代理 通过按键 定义 可以 曾经 执行此功能。只有设计得当的自然钥匙才能做到这一点。(如果您所拥有的只是一个代理键,那么只要您给它一个不同的代理键值,您就可以添加另一个与现有行完全相同的属性行)
    2. 其次,它们在其他表中用作外键的引用(指针),这些表是具有主键的表中实体的子实体。自然键(特别是如果它是多个属性的组合)对于这个函数来说不是一个好的选择,因为这意味着a)所有子表中的外键也必须是组合键,使它们非常宽,从而降低了所有约束操作和SQL联接的性能。和b)如果键的值在主表中发生了更改,则需要对值表示为FK的每个表进行级联更新。

    所以答案很简单… 总是 (无论您在何处关注数据完整性/一致性)使用自然密钥,必要时使用两者!当自然键是复合键、长键或不够稳定时,添加备用代理键(例如自动递增整数),用作子表中FK的目标。但是,在丢失表的数据一致性的风险下,不要从主表中删除自然键。

    为了让这个水晶清晰可见,让我们举个例子。 假设你有一张有银行账户的桌子…自然密钥可以是银行路由号和银行帐号。为了避免在Transactions表中的每个事务记录中使用此双组合键,您可能会决定在BankAccount表上放置一个人工生成的代理键,它只是一个整数。但你最好保留自然钥匙!如果没有,如果您也没有复合自然键,那么您很容易在表中得到两行,如下所示

    id  BankRoutingNumber BankAccountNumber   BankBalance
     1     12345678932154   9876543210123       $123.12
     2     12345678932154   9876543210123    ($3,291.62)
    

    现在,哪一个是对的?

    在下面的评论中,对马克来说,能够“识别 “?在我看来根本不好,因为我们需要识别的是 银行账户 行代表! 标识行 只对内部数据库技术功能(如查询中的联接)或FK约束操作很重要,如果需要,这些功能应该使用代理键,而不是自然键。

    您是对的,因为对自然键的糟糕选择,或者有时甚至是对自然键的最佳可用选择,可能不是真正唯一的,或者不能保证防止重复。但是,任何选择都比没有选择要好,因为它至少会防止为选择为自然键的属性中的相同值重复行。通过适当选择关键属性,这些问题可以保持在最低限度,但有时它们是不可避免的,必须加以处理。但这样做还是比允许不正确、不准确或冗余的数据进入数据库要好。

    至于“易用性”,如果您使用的所有自然键都是约束重复行的插入,而您使用的是另一个、代理项、键作为FK约束的目标,那么我看不到任何易用性问题。

        3
  •  4
  •   user151841 zhanxw    15 年前

    数据库纯粹主义者之所以对代理键大发雷霆,是因为如果使用不当,它们可以允许数据复制,这是良好的数据库设计注定要消除的一个缺点。

    例如,假设我有一个邮件列表的电子邮件地址表。我希望它们是独一无二的,对吧?在相同的电子邮件地址中输入2、3或n项是没有意义的。如果我使用 email_address 作为我的主键(这是一个自然键——它作为独立于您创建的数据库结构的数据存在),这将保证我的邮件列表中永远不会有重复的电子邮件地址。

    但是,如果我有一个字段 id 作为代理密钥,我可以拥有任意数量的重复电子邮件地址。如果同一个电子邮件地址有10行,而其他列中的所有订阅信息都有冲突,那么这将变得很糟糕。哪一个是正确的,如果有的话?没办法说!在这之后,您的数据完整性就诞生了。没有办法修复数据,只能逐个查看记录,询问人们哪些订阅信息是真正正确的,等等。

    非纯粹主义者之所以需要它,是因为它使使用标准化代码变得容易,因为您可以依赖于引用具有整数值的单个数据库行。如果你有一把自然的钥匙,比如说, ( client_id, email, category_id ) ,程序员将讨厌围绕这个实例进行编码!它打破了基于类编码的封装,因为它要求程序员对表结构有深入的了解,而一个删除方法可能对每个表有不同的代码。讨厌!

    很明显这个例子过于简化了,但它说明了这一点。

        4
  •  3
  •   Randy Minder    15 年前

    哇,你用这个问题打开了一罐虫子。数据库纯粹主义者会告诉你永远不要使用代理键(就像上面提到的那样)。另一方面,代理键可以有一些巨大的好处。我一直在用它们。

    在SQL Server中,代理项通常是SQL Server为您生成的自动增量标识值。它与表中存储的实际数据没有关系。与之相反的是一把天然钥匙。例如社会保险号码。这确实与表中存储的数据有关系。自然键有好处,但在我看来,使用代理键的好处大于自然键。

    我注意到在您的示例中,您有一个主键的GUID。您通常希望远离作为主键的guid。它们大而笨重,通常可以随机插入数据库中,导致严重的碎片化。

    兰迪

        5
  •  1
  •   Petrus Theron    15 年前

    用户表

    使用A Guid 作为您的 Users 表是 很完美 .

    登录表

    除非你打算暴露 LogEntry 数据到外部系统或与另一个数据库合并,我只需使用递增的 int 而不是 GUID 作为主键。它更容易使用,并且使用的空间会稍微少一些,这在长达数年的巨大原木中可能是非常重要的。

        6
  •  0
  •   Alex    15 年前
    • 主要的关键是你做了什么。无论您将什么定义为主键,都是主键。通常是一个整数id字段。
    • 代理键也是这个ID字段。它是自然键的替代项,自然键根据应用程序数据定义唯一性。

    将一个整数ID作为主键(即使它实际上并不意味着什么)的背后的想法是为了索引。然后您可能会将自然键定义为表上的唯一约束。这样你就可以得到两个世界中最好的。使用ID字段进行快速索引,并且每一行仍然保持其自然的唯一性。

    也就是说,有些人用天然钥匙发誓。

        7
  •  0
  •   Tom H zenazn    15 年前

    实际上有三种钥匙可以谈。主键是用来唯一标识表中每一行的。代理键是使用该属性创建的人工键。自然密钥是从实际生活数据中派生出来的主密钥。

    在某些情况下,自然密钥可能不方便,因此可以创建一个代理密钥用作外键等。例如,在日志或日记中,pk可能是条目的日期、时间和全文(如果可以同时添加两个条目)。显然,每次想要标识一行时都要使用所有这些内容是一个坏主意,因此您可以创建一个“日志ID”。它可能是一个序列号(最常见的),也可能是日期加上一个序列号(如20091222001),或者它可能是其他东西。但是,有些自然钥匙也可以作为主钥匙使用,例如车辆VIN号、学生ID号(如果不重复使用),或者在连接表的情况下,连接两个表的PK。

    这只是表键选择的概述。有很多事情需要考虑,尽管在大多数商店中,你会发现它们都与“向每个表添加一个标识列,这是我们的主键”匹配。然后你会遇到所有的问题。

    在您的情况下,我认为日志项的logentryID似乎是合理的。用户表的ID是否为FK?如果不是这样,那么我可能会质疑ID和logentryID是否在同一个表中,因为它们是多余的。如果是的话,我会把名字改成用户名或者类似的东西。