我继承了一个数据库,其构思是复合键比使用唯一的对象ID字段更理想,并且在构建数据库时,永远不应将单个唯一ID 用作主键.因为我正在为这个数据库构建一个Rails前端,所以我遇到了使它符合Rails约定的困难(虽然可以使用自定义视图和一些额外的gem来处理复合键).
编写它的人的这种特定模式设计背后的原因与数据库如何以非有效方式处理ID字段有关,并且当它构建索引时,树类排序存在缺陷.这个解释没有任何深度,我仍然试图围绕这个概念(我熟悉使用复合键,但不是100%的时间).
任何人都可以提供意见或为此主题添加更深入的内容吗?
大多数常用引擎(MS SQL Server,Oracle,DB2,MySQL等)使用代理键系统不会遇到明显的问题.有些甚至可能通过使用代理来提升性能,但性能问题是高度特定于平台的.
一般而言,自然密钥(以及通过扩展,复合密钥)与代理密钥辩论相比具有悠久的历史,并且看不到"正确答案".
自然键(单数或复合)的参数通常包括以下内容:
1) 它们已在数据模型中可用.被建模的大多数实体已经包括满足密钥需求的一个或多个属性或属性组合,以便创建关系.为每个表添加附加属性包含不必要的冗余.
2) 它们消除了对某些连接的需要.例如,如果您的客户包含客户代码,并且发票上有发票编号(两者都是"自然"键),并且您想要检索特定客户代码的所有发票编号,则只需使用即可"SELECT InvoiceNumber FROM Invoice WHERE CustomerCode = 'XYZ123'"
.在经典的代理键方法中,SQL看起来像这样:"SELECT Invoice.InvoiceNumber FROM Invoice INNER JOIN Customer ON Invoice.CustomerID = Customer.CustomerID WHERE Customer.CustomerCode = 'XYZ123'"
.
3) 它们有助于实现更普遍适用的数据建模方法.使用自然键,可以在不同的SQL引擎之间使用相同的设计.许多代理关键方法使用特定的SQL引擎技术进行密钥生成,因此需要更加专业化的数据模型才能在不同平台上实现.
代理键的参数倾向于围绕SQL引擎特定的问题:
1) 当业务需求/规则发生变化时,它们可以更轻松地更改属性.这是因为它们允许将数据属性隔离到单个表中.对于没有有效实现标准SQL结构(如DOMAIN)的SQL引擎而言,这主要是一个问题.当DOMAIN语句定义属性时,可以使用ALTER DOMAIN语句在模式范围内执行对属性的更改.不同的SQL引擎在更改域时具有不同的性能特征,并且一些SQL引擎根本不实现DOMAINS,因此数据建模者通过添加代理键来补偿这些情况,以提高对属性进行更改的能力.
2) 它们比自然键更容易实现并发.在自然键情况下,如果两个用户同时使用相同的信息集(例如客户行),并且其中一个用户修改了自然键值,则第二个用户的更新将失败,因为他们是客户代码更新不再存在于数据库中.在代理键情况下,更新将成功处理,因为不可变ID值用于标识数据库中的行,而不是可变的客户代码.但是,并不总是希望允许第二次更新 - 如果客户代码发生了变化,则可能不允许第二个用户继续进行更改,因为行的实际"身份"已更改 - 第二个用户可能正在更新错误的行.代理键和自然键本身都没有解决这个问题.
3) 它们比自然键表现更好.性能最直接受SQL引擎的影响.由于SQL引擎数据存储和检索机制,使用不同SQL引擎在相同硬件上实现的相同数据库模式通常具有显着不同的性能特征.一些SQL引擎非常接近平面文件系统,当数据库模式中的多个位置出现相同的属性(如客户代码)时,数据实际上是冗余存储的.当需要对数据或模式进行更改时,SQL引擎的这种冗余存储可能会导致性能问题.其他SQL引擎在数据模型和存储/检索系统之间提供了更好的分离,允许更快地更改数据和模式.
4) 代理键对某些数据访问库和GUI框架有更好的作用.由于大多数代理键设计的同质性(例如:所有关系键都是整数),数据访问库,ORM和GUI框架可以使用信息而无需数据的特殊知识.自然键由于其异构性(不同的数据类型,大小等),与自动或半自动工具包和库无法一起使用.对于特殊场景,例如嵌入式SQL数据库,可以接受使用特定工具包设计数据库.在其他场景中,数据库是企业信息资源,由多个平台,应用程序,报告系统和设备同时访问,因此在设计时关注于任何特定库或框架时,其功能不佳.此外,
我倾向于落在自然键的一边(显然),但我并不狂热.由于我工作的环境,我帮助设计的任何给定数据库可能被各种应用程序使用,我使用自然键进行大多数数据建模,很少引入代理.但是,我不会试图重新实现使用代理的现有数据库.代理关键系统工作得很好 - 无需改变已经运行良好的东西.
有一些很好的资源可以讨论每种方法的优点:
http://www.google.com/search?q=natural+key+surrogate+key
http://www.agiledata.org/essays/keys.html
http://www.informationweek.com/news/software/bi/201806814
我已经开发了15年的数据库应用程序,我还没有遇到过一个非代理密钥比代理密钥更好的选择.
我不是说这种情况不存在,我只是说当你考虑实际开发访问数据库的应用程序的实际问题时,通常代理键的好处开始压倒非理论的纯度-surrogate键.
主键应该是不变的,毫无意义的 ; 非代理键通常会失败一个或两个要求,最终
如果密钥不是常量,那么您将来会遇到更复杂的更新问题
如果密钥没有意义,那么它更可能改变,即不是恒定的; 往上看
举一个简单的常见例子:库存项目表.将项目编号(sku编号,条形码,零件代码或其他任何东西)作为主键可能很诱人,但一年后所有项目编号都会发生变化,并且您将面临一个非常混乱的更新 - 整个 -数据库问题......
编辑:还有一个比哲学更实用的问题.在许多情况下,你会以某种方式找到一个特定的行,然后再更新它或再次找到它(或两者).使用复合键,有更多数据可以跟踪WHERE子句中的更多数据以及重新查找或更新(或删除).在此期间,其中一个关键部分也可能已发生变化!使用代理键,始终只保留一个值(代理ID),并且根据定义它不能更改,这显着简化了情况.
这听起来像创建数据库的人是在自然键和自由键辩论的自然键侧.
我从来没有听说过关于ID字段的btree的任何问题,但我也没有深入研究它...
我落在代理键侧:使用代理键时重复次数较少,因为您只在其他表中重复单个值.由于人类很少手工加入桌子,我们不在乎它是否是一个数字.此外,由于在索引中只能查找一个固定大小的列,因此可以安全地假设代理也可以通过主键查找更快的查找时间.
使用'unique(object)ID'字段简化了连接,但是您应该将另一个(可能是复合的)键保持唯一 - 不要放松非空约束并且DO保持唯一约束.
如果DBMS无法有效处理唯一整数,则存在很大问题.但是,同时使用"唯一(对象)ID"和另一个键确实比其他键使用更多空间(对于索引),并且在每个插入操作上都有两个索引要更新.所以它不是免费赠品 - 但只要你保持原来的钥匙,那么你就行了.如果你取消了另一把钥匙,你就会破坏你的系统设计; 所有的地狱最终都会破裂(你可能会或可能不会发现地狱破裂了).
我基本上是代理关键团队的成员,即使我欣赏和理解JeremyDWill在这里提出的论点,我仍然在寻找"自然"键比代理更好的情况......
处理此问题的其他帖子通常涉及关系数据库理论和数据库性能.另一个有趣的论点,在这种情况下总是被遗忘,与表规范化和代码生产率有关:
每次我创建一张桌子,我都会失去时间
识别其主键及其物理特征(类型,大小)
每次我想在我的代码中引用它时记住这些特性?
向团队中的其他开发人员解释我的PK选择?
我对所有这些问题的回答都是否定的:
在处理人员清单时,我没有时间试图找出"最好的主键".
我不想记住我的" computer
"表的主键是64个字符长的字符串(Windows是否接受计算机名称的多个字符?).
我不想向其他开发人员解释我的选择,其中一个人最终会说"是的,但是考虑到你必须管理不同域名的计算机?这64个字符的字符串是否允许你存储域名+电脑名称?"
所以我在过去的五年里一直在用一个非常基本的规则工作:每个表(我们称之为' myTable
')的第一个字段叫做' id_MyTable
',它是uniqueIdentifier类型的.即使这个表支持"多对多"关系,例如' ComputerUser
'表,' id_Computer
'和' id_User
' 组合形成一个非常可接受的主键,我更喜欢创建这个' id_ComputerUser
'字段作为uniqueIdentifier,只是坚持规则.
主要优点是您不必关心代码中主键和/或外键的使用.获得表名后,就会知道PK名称和类型.一旦知道数据模型中实现了哪些链接,您就会知道表中可用外键的名称.
我不确定我的规则是最好的.但这是一个非常有效的!