每个表都应该有一个单字段代理/人为主键吗?


33

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

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

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


Answers:


28

我会说不,并非总是如此,但大多数时候是。

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

  • 纯交集表。如果存在将交集作为外键目标的风险,并且几乎没有交集吸引独立属性的风险(即,两个父表中不包含FK的事物),则可以避免使用组合信心十足的FK担任PK。
  • 具有静态业务键的查找表。如果您有一个
    具有唯一业务密钥的查找表,该查询密钥固定在您的
    业务外部,并且出于任何 实际目的进行更改的可能性为零
    ,那么直接使用业务密钥可以使
    事情变得简单。例如州或省
    代码列表或ANSI标准编号列表等。
  • 包含从多个独立来源合并的数据的表。如果您的系统有很多数据源,必须将它们汇总到一个表中(例如在总部),那么有时您需要一个复合键,其中应包括源系统键值和指示源系统是什么的代码。

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

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

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

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


“使用代理密钥将使您避免陷入陷阱”问题不是关于代理vs自然,而是关于单字段与多字段代理。
杰克·道格拉斯

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

我没有改变这个问题,你可以从历史看,只是增加了重点,我认为这将有助于脱脂阅读器
杰克·道格拉斯

12

没有。

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

我将在以下示例中说明我的观点,其中:

  • brand 是汽车品牌,例如福特,丰田等
  • dealer 是与品牌相关的实体经销店(例如仅销售福特的福特经销店)
  • model 是汽车的类型,例如福特福克斯,福特嘉年华等
  • stock 每个经销商当前的前院车数

如果我们为dealer和创建一个单字段代理键,model如下所示:

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) )

那么可以在其中插入一行stock,将福特链接dealer到“丰田”模型。添加brand_id references brandstock只能使问题变得更糟。另一方面,如果我们将外键保留为主键的一部分,如下所示:

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 )

现在,“福特”经销商只能库存“福特”汽车的规则已由模型自然执行。

请注意,在“复合键”示例中dealer_id,根据偏好,它可以是唯一的,也可以不是唯一的。它不必是唯一的(即备用键),但是通过使其变得唯一(可能有一点存储空间)而损失很少,并且它非常方便,因此这是我通常设置的方式,例如:

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

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

1
因此,“您的业务规则可能会更改”。任何业务规则都是如此,并且始终很难进行重新建模。我通常会被告知业务规则是什么-我不会自己决定。
杰克·道格拉斯

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

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

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

12

“这取决于”

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

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

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

编辑:再次阅读问题

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

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.