我应该使用多列主键还是添加新列?


15

我当前的数据库设计使用多列主键来使用现有数据(无论如何都是唯一的),而不是创建为每个条目分配任意键的附加列。我知道这是允许的,但我想知道这是否是我可能要谨慎使用并可能避免的做法(就像C中的goto)。

那么,我可能会在这种方法中看到哪些缺点,或者是我想要一个单列键的原因呢?


2
我不知道,我认为这样做会更好。
FrustratedWithFormsDesigner

2
@FrustratedWithFormsDesigner可以解决问题,但我认为它在这里也可以工作,因为问题的重点似乎在“此方法的优缺点”而不是“我怎么做X?”。
亚当李尔

@Anna Lear♦:这是关于设计决策的“利弊”,它将对编码产生直接而明确的影响,因此我认为SO会是一个更好的地方。
FrustratedWithFormsDesigner

Answers:


8

通常,当您有一个带有多列主键的表时,这是联接表(多对多)的结果,该表被提升为自己的实体(因此值得拥有自己的主键)。有很多人认为默认情况下任何联接表都应该是实体,但这是另一天的讨论。

让我们看一下假设的多对多关系:

学生* --- *班级

(一个学生可以在多个班级中,一个班级可以有多个学生)。

在这两个表之间将是一个称为StudentClass(或ClassStudent,取决于您如何编写)的联结表。有时,您想跟踪学生上课时的情况。因此,您将其添加到StudentClass表中。至此,StudentClass已经成为一个独特的实体...并且应该被赋予一个名称来识别它,例如注册。

学生1 --- *入学人数* --- 1班

(一个学生可以有多个注册,每个注册都针对一个班级(或者相反,一个班级可以拥有多个注册,每个注册都针对一个学生)。

现在,您可以查询类似的信息,例如,去年有多少学生参加了化学101课程?或者,约翰·多伊(John Doe)参加Acme University的学生参加了哪些课程?如果没有单独的主键,这是可能的,但是一旦您有了注册的主键,一个简单的查询就会是这些注册(按ID),有多少学生获得及格分数?

确定某个实体是否值得PK的决定归结为您将对该实体进行多少查询(或操作)。举例来说,您想将为某位学生完成的作业附在班上。附加此实体(分配)的逻辑位置将在“注册”实体上。为注册提供自己的主键将使Assignment查询更简单。


1
因此,您将其添加到StudentClass表中。至此,StudentClass已经成为一个独特的实体...并且应该被赋予一个名称来识别它,例如注册。这是一件很简单的事情,但是这样做有很多价值!
Botis

8

有一个单独的id列很有意义。当您想从数据库表中获取某些信息时,这样做会更容易:

SELECT whatever FROM table WHERE id=13

比从表中选择任何内容都更合适col1 ='val1'AND col2 ='val2'AND col3 ='val3'

例如,在Web应用程序中,它将转换为如下所示的URL:

www.somewebsite.com/somepage.php?id=13

或像这样:

www.somewebsite.com/somepage.php?col1=val1&col2=val2&col3=val3

4
当您可以在一个ID上链接而不是几列时,添加相关表要容易得多
CaffGeek 2011年

3
抱歉,在这一点上我必须为-1,因为A)它不是黑白的。添加ID列会带有否定词,例如您何时何地生成新ID。另外,它可能导致额外的联接或SELECT查询。而且,B),我不知道这实际上是如何导致任何类型的URL要求的(除非您使用的框架不好)。我的网址中没有任何查询字符串?id=13,更不用说了?col1=val1&col2=val2&col3=val3
妮可

2
@renesis:该站点在URL中有唯一的问题和用户。虽然,这有点特殊,因为特定数据不会更改。
Michael K

1
@Renesis,大多数(也许是所有)现代数据库具有auto_increment整数列类型,可以自动安全地生成ID并通过sql查询或库函数调用将其报告回来。或在分布式环境中,您使用大型随机哈希。如果您的表中还没有ID,则某些DB甚至会为您创建一个隐藏的 ID列。
GrandmasterB

@Michael-我不是说ID永远不会出现在URL中。当然可以。如果您有代表一行数据的URL,那么是的,该数据可能应该具有唯一的ID。除非URL的其他部分已经提供了多键的其他部分。@GrandmasterB我工作的最后两家公司(超过6年)都没有使用MySQL(也支持Oracle和SQL Server的一家)使用自动增量,也没有使用大型随机散列。
妮可

8

基本上,您是在问是否应该使用代理键或自然键(在您的情况下,听起来像复合自然键)。这是一篇很棒的文章:http : //www.agiledata.org/essays/keys.html

我更喜欢替代密钥,因为它们可以简化数据库生命周期内的管理(您不必担心密钥更改含义的隐含性,这种含义永远不会发生,但是在涉及人类的任何实际系统中都是如此)。 但是,如果数据库中有很多“查找”表(即基本上是键:值对的表),则代理键会变得很麻烦,因为必须将这些表加入查询中才能获得有意义的结果。

例如,假设您有两个实体:地址和国家/地区。

  • 关系为:地址* ----- 1国家
  • 国家实体基本上是一个键:值对(例如,美国:美国,加拿大:加拿大,墨西哥:墨西哥等)
  • 要查询此结构以查询美国的所有地址,请执行以下操作:

select * from Address where CountryCode = 'US'

  • 要使用代理键执行相同的查询:

select Address.* from Address join Country on Address.CountryID = Country.ID where Country.Code = 'US'

如果我很确定自然键不会经常更改(如果有的话),我很乐意为查找表强制使用自然键,而对其他所有命令都使用代理键。


5

这取决于您如何访问数据。如果您进行了大量的部分键查询(在其中仅基于说三个键中的两个来选择记录),则需要保留多部分键。OTOH,如果您与其他表具有许多1:1关系,那么拥有替代键可能更有意义。


1

我喜欢始终为每个表使用代理主键。但是我听说并没有很多“硬”的理由来执行此操作。

我曾经一次被多列自然键咬过是在ORM。有时,使用Linq To Entities时,多列主键会出现问题。


1

永远不要说永远不要,但是加入4列很痛苦。您拥有智能数据的列越多,这些值发生变化的可能性就越大。可以设置数据库以通过级联更新来维护参考完整性。

您始终可以创建另一个索引来处理唯一值。

在大多数情况下,性能可能微不足道,但是无论是否使用副键,您都可以测试查询。


0

我发现很难提出一个合理的理由来授权一个单独的密钥,但是就像您说的那样,很多人都将它放入了。

在处理事实/明细表时,我没有找到任何帮助(特别是在存储方面)。一个典型的示例是具有(customer_key,store_key,product_key)和数量的销售事实表,没有记录级别的键没有多大意义。



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.