我了解代理/人工密钥的一大好处-它们不会更改,因此非常方便。无论是单个字段还是多个字段,这都是正确的-只要它们是“人工”字段即可。

但是,有时以自动递增的整数字段为主变量似乎是一个政策问题每个表的键。拥有这样一个单字段密钥始终是最好的主意吗?为什么(或为什么不这样做)?

要明确,这个问题不是关于人工还是自然的问题,而是关于所有人工密钥是否应该成为单一字段

评论

另请参见dba.stackexchange.com/a/112632/1396

另请参阅softwareengineering.stackexchange.com/q/204521/32523

#1 楼

我要说的不是,不是总是这样,但大多数时候是。.

在某些情况下,您不需要代理键或人工键:



纯交点表。如果没有
交叉点成为外键目标的风险,并且
几乎没有
交叉点吸引独立属性的风险
(即,除了FK之外,两者都是父表),那么您
就可以放心地将FK组合用作具有公平信心的PK。

具有静态业务密钥的查找表。如果您有一个具有唯一业务密钥的查找表,该业务密钥在外部固定在您的业务上,并且出于任何实际目的进行更改的可能性为零,那么直接使用业务密钥可以使事情变得更简单。一个示例可能是州或省份代码列表或ANSI标准编号列表等。

包含从多个独立的
来源合并的数据的表。如果您的系统有许多数据源,必须将这些数据源合并到一个表中,例如在总部,那么
有时您需要一个复合键,其中应包括源系统键
值和指示源系统是什么的代码。

在某些情况下,忠实于单调递增的整数代理键也不理想。您可以使用字母数字替代键。这些可能包括:


您需要合并来自多个独立
源的数据的情况。为避免按键冲突,您可以使用GUID代替
IDENTITY键。
您被迫使用非数字键表示的情况。假设您有一个车牌数据库。
您的密钥可以是字母数字值,而不是纯数字。
某些外部要求迫使您应用的情况
压缩到您的关键值。可以使用6个基数36位数字来代替
int32的10位数字。

为什么大多数时候是的?该问题的最根本答案是,如果您需要修改任何表上的主键值,那简直是地狱。可以想象,由于用户可以看到或触摸的几乎所有东西都可能在某个时刻进行更新,因此使用可见键值会引起混乱。使用代理密钥可以防止您陷入陷阱。

话虽如此,请记住,YAGNI在应用此概念时仍有空间。您无需将带有IDENTITY键的代码表强加到架构的每个角落,以防万一有人认为员工表中的男性符号需要从M更改为X或有些愚蠢的情况。

评论


“使用代理密钥将使您避免陷入陷阱”问题不在于代理与自然,而是单场与多场代理。

–杰克·道格拉斯(Jack Douglas)
2011年9月24日在12:04

正如您在对自己的答案的评论中所承认的那样,对于数据库设计中的审慎性,我们处于“不同意”领域。根据您的编辑,我不会在代理键和自然键之间进行区分,因此我的第二个要点是迟到的话题。关于何时可以脱离经典身份/顺序方法的其他观点仍然存在。

–乔尔·布朗(Joel Brown)
2011年9月24日在12:19

从历史上可以看出,我并没有改变这个问题,只是在强调我认为会帮助略读读者的地方增加了重点

–杰克·道格拉斯(Jack Douglas)
2011-09-24 14:50

#2 楼

“取决于”

是:当自然键为宽且非数字时,替代IDENTITY / AUTONUMBER字段会很好。注意:这假定默认情况下在SQL Server和Sybase等中默认发生“ PK”和聚集索引的合并。

否:当两个父键足够时,许多表很多。或者,当自然键较短且固定长度时,例如货币代码

当然,脑残的ORM(读取:(n)休眠)可能会胜过这些规则...

编辑:再次阅读问题

具有2个代理父键的多/许多表将具有多列PK。
但是,它不需要另一个代理列。

如果一个表确实具有代理(IDENTITY等)键,则它不必是多列。

您可以有一个包含代理的超级键,但这将强制执行其他规则(例如,子类型)

#3 楼



我肯定会说,单字段键至少在复合键方面不如复合键重要。这并不是说,如果愿意的话,也就不应该具有单字段代理键,但是我个人更喜欢将最常用作外键目标的键称为主键

在下面的示例中,我将尝试阐明我的观点,其中:



brand是汽车品牌,例如福特,丰田等

dealer是与品牌相关的实体经销店(例如仅销售福特的福特经销店)

model是汽车的类型,例如福特福克斯,福特嘉年华等

/> stock是每个经销商当前的前庭车辆数量br />
,则可以在dealer中插入一行,从而将福特model链接到“丰田”模型。将stock添加到dealer只会使问题变得更糟。另一方面,如果我们将外键保留为主键的一部分,如下所示:

create table brand( brand_id integer primary key );

create table dealer( dealer_id integer primary key, 
                     brand_id integer references brand )

create table model( model_id integer primary key, 
                    brand_id integer references brand )

create table stock( model_id integer references model, 
                    dealer_id integer references dealer, 
                    quantity integer,
                      primary key(model_id, dealer_id) )


现在的规则是,“福特”经销商只能库存“福特”

请注意,在“复合键”示例中,根据偏好,brand_id references brand可能是唯一的,也可能不是唯一的。它不必是唯一的(即备用键),但是通过使其变得唯一(可能有一点存储空间)而损失的很少,并且它非常方便,这就是我通常设置它的方式,例如: br />
create table brand( brand_id integer primary key );

create table dealer( brand_id integer references brand, 
                     dealer_id integer, 
                       primary key(brand_id, dealer_id) )

create table model( brand_id integer references brand, 
                    model_id integer, 
                      primary key(brand_id, model_id) )

create table stock( brand_id integer, 
                    model_id integer, 
                    dealer_id integer, 
                    quantity integer,
                      primary key(brand_id, model_id, dealer_id),
                      foreign key(brand_id, model_id) references model,
                      foreign key(brand_id, dealer_id) references dealer )


评论


考虑到关于示例的通常附带条件,不一定从各个角度都是完美的,我想说这种类型的设计特别脆弱。虽然找到一种使用DRI实施业务规则的方法令人满足,但它也剥夺了您对变更做出响应的能力。如果丰田购买福特汽车,或者即使福特经销商决定出售二手丰田汽车,那么由DRI驱动的业务规则也会给您带来维修方面的麻烦。

–乔尔·布朗(Joel Brown)
2011年9月24日,下午1:29

因此,“您的业务规则可能会更改”。对于任何业务规则而言都是如此,并且始终很难重新建模。我通常会被告知业务规则是什么-我不会自己决定。

–杰克·道格拉斯(Jack Douglas)
2011-09-24 5:45

您是说完全不应该使用DRI来执行业务规则吗?这不是DRI唯一的功能吗? -即使简单的外键也是DRI强制执行的业务规则。

–杰克·道格拉斯(Jack Douglas)
2011-09-24 9:58

当然,DRI会强制执行业务规则。您必须决定要在代码中强制执行哪些规则,以及在架构中要强制执行哪些规则。模式更改几乎总是比代码更改难。数据模型中可以包含两种业务规则。一个属于那里,一个不属于。对于您的业务而言重要的数据的基本性质不会发生很大变化。处理该数据的特定方式更加不稳定。像汽车拥有制造商这样的规则属于数据模型。像经销商这样的规则永远不会出售两个品牌的汽车。

–乔尔·布朗(Joel Brown)
2011年9月24日上午11:48

“模式更改几乎总是比代码更改更难” IMO相反。实际上,我实际上不同意您刚才所说的所有内容,但我怀疑是否有任何理由与您争吵,因此我将其保留。

–杰克·道格拉斯(Jack Douglas)
2011年9月24日在12:03