复合主键是一个坏习惯吗?[关闭]


14

我想知道复合主键是否是一种不好的做法,如果不是,则建议在哪种情况下使用。

我的问题是基于这篇文章

数据库设计错误

关于复合主键的部分:

错误做法6:复合主键

这是一个有争议的观点,因为当今许多数据库设计人员都在谈论使用整数ID自动生成的字段作为主键,而不是使用由两个或多个字段的组合定义的复合键。目前,这被定义为“最佳实践”,就我个人而言,我倾向于对此表示赞同。

复合主键的图像

但是,这只是一个约定,当然,DBE允许定义复合主键,许多设计人员认为这是不可避免的。因此,与冗余一样,复合主键是设计决策。

但是请注意,如果带有复合主键的表预期有数百万行,则控制复合键的索引可能会增长到CRUD操作性能大大下降的地步。在那种情况下,最好使用一个简单的整数ID主键,其索引将足够紧凑,并建立必要的DBE约束以保持唯一性。


4
这不是“好”或“坏”的做法。每个设计决定都必须达到目的。如果您可以(向自己和他人)解释为什么需要复合PK,那很好。相反,如果您可以解释为什么不需要它,那么也很好。在我看来,您链接到的文章在解释方面做得很差。
mustaccio

本文说明了这一点,但是如果我们以其“最佳实践”中的流行框架(例如rails)来看,则不支持这种类型的主键,所以我问为什么?这是出于技术难题或其他原因。
hackvan '17

对于框架设计而言,仅支持“简单”的单列整数主键更为容易。而且由于大多数开发人员(至少以我的个人经验)在数据库技能方面(至少相对于本网站的用户而言)没有太多知识,因此对于大多数软件用户而言,它的运行状况已经足够好。由于大多数软件用户不需要组合键(或者至少在开始时就认为他们不需要),因此他们可以不必为组合键提供(良好)支持而摆脱困境。
Willem Renzema '17

1
GUID如何比INTEGER [Serial | 自动递增| 身份| <whatever_integer_you_like>]?
Vérace

4
我不会雇用那个作者
狗仔队

Answers:


31

说使用"Composite keys as PRIMARY KEY is bad practice"完全是胡说八道!

复合物PRIMARY KEY通常是一件非常“好事”,并且是模拟日常生活中自然情况的唯一方法!

想一想经典的Databases-101学生和课程教学示例以及许多学生参加的许多课程!

创建表格课程和学生:

CREATE TABLE course
(
  course_id SERIAL,
  course_year SMALLINT NOT NULL,
  course_name VARCHAR (100) NOT NULL,
  CONSTRAINT course_pk PRIMARY KEY (course_id)
);


CREATE TABLE student
(
  student_id SERIAL,
  student_name VARCHAR (50),
  CONSTRAINT student_pk PRIMARY KEY (student_id)
);

我将以PostgreSQL方言(和MySQL)为您提供示例-只需稍作调整即可在任何服务器上使用。

现在,您显然想要跟踪哪个学生正在学习哪门课程-因此您有了所谓的joining table(也称为linkingmany-to-manym-to-n表格)。它们associative entities在更多技术术语中也被称为!

1门课程可以有很多学生。
1名学生可以参加许多课程。

因此,您创建一个联接表

CREATE TABLE course_student
(
  cs_course_id INTEGER NOT NULL,
  cs_student_id INTEGER NOT NULL,

  -- now for FK constraints - have to ensure that the student
  -- actually exists, ditto for the course.

  CREATE CONSTRAINT cs_course_fk FOREIGN KEY (cs_course_id) REFERENCES course (course_id),
  CREATE CONSTRAINT cs_student_fk FOREIGN KEY (cs_student_id) REFERENCES student (student_id)
);

现在,明智地给这张桌子加个表的唯一方法PRIMARY KEY是将KEY课程和学生结合起来。这样,您将无法获得:

  • 学生和课程组合的副本

    • 一门课程只能让同一名学生注册一次,并且

    • 一个学生只能一次注册同一门课程

  • 您还可以KEY对每位学生的课程进行现成搜索- 又称覆盖率索引

  • 找不到没有学生的学生和没有上课的学生的课程很简单!

    -db-fiddle 示例将PK约束折叠到了CREATE TABLE中。我更喜欢CREATE TABLE语句中包含所有内容。


ALTER TABLE course_student 
ADD CONSTRAINT course_student_pk 
PRIMARY KEY (cs_course_id, cs_student_id);

现在,如果您发现按课程搜索学生的速度很慢,可以使用UNIQUE INDEXon(sc_student_id,sc_course_id)。

ALTER TABLE course_student 
ADD CONSTRAINT course_student_sc_uq  
UNIQUE (cs_student_id, cs_course_id);

没有灵丹妙药添加索引-他们作出INSERTS和UPDATEs ^慢,但在巨大的大有裨益下降SELECT倍!它是由开发人员决定给他们的知识和经验指标,但要说复合PRIMARY KEYs为总是不好是完全错误的。

对于联接表,通常只有 它们才有PRIMARY KEY意义!连接表也经常是建模业务,自然或几乎我能想到的每个领域中发生的事情的唯一方法!

此PK也covering index可用作帮助加快搜索速度的。在这种情况下,如果人们经常在(course_id,student_id)上进行搜索,那将是特别有用的,这是人们可能会想到的!

这只是一个小例子,说明组合PRIMARY KEY可能是一个很好的主意,并且是模拟现实的唯一明智的方法!关闭我的头顶,我能想到的很多很多了。

我自己的作品中的一个例子!

考虑一个航班表,其中包含flight_id,出发和到达机场列表以及相关时间,然后还有一个机组人员的cab_crew表!

可以建模的唯一明智的方法是有一个flight_crew表,其中的flight_id和crew_id为attibutes,唯一明智的方法PRIMARY KEY是使用两个字段的组合键!


2
在课程和学生的示例中,course_student是否有可能id作为主键和上的唯一索引cs_student_id cs_course_id并获得相同的结果?
hackvan '17

2
为什么要浪费资源呢?通过PK(course_id,student_id),根据定义,您已经在这些字段上具有唯一索引!(student_id,course_id)上的唯一索引可能会用于加快搜索速度-例如,如果您要寻找的是没有上任何课程的学生,但是该决定可能是可操作的,但是在如今存储相对便宜的情况下,我会对此加以补充,特别是因为有人会认为该表不会非常频繁地更新。
Vérace

1
完全同意链接表-我现在正在与几个人合作。但是,当我戴上C#帽子时,我正在使用reversepoco generator并为下一层构建有用的类(查找,保存等)。我遇到了一个主要问题-复合键成为具有任何通用保存/查找代码的PITA。是的,也许我可以返回EDMX文件,但是我仍然需要解决特殊情况的代码(计数Pkey列?)或添加人工代理键​​(不喜欢,并且需要其他唯一性约束:()。)不喜欢复合材料的人们正在使用App层代码讲话
理查德·格里菲思

根据插入的频率和索引碎片整理与维护窗口的频率,这是更好的解决方案。但是某些设计选择是折衷的折衷方案,这些要求可能不会立即显现出来。但是正如一个评论所说,确定两种方案的利弊,并做出设计选择。
乔纳森·菲特

学生重修课程会怎样?然后,除非按时间分隔的课程获得不同的ID,否则您将拥有另一个映射表。或添加课程日期字段,现在必须将其添加到密钥中。
iheanyi

3

我的观点:一半,“主键”不一定是用来在表中查找数据的唯一键,尽管数据管理工具会将其作为默认选择。因此,要选择是将两列的组合还是随机(可能是串行)生成的数字作为表键,可以一次拥有两个不同的键。

如果数据值包含一个可以表示该行的合适的唯一术语,那么我宁愿将其声明为“主键”(即使是复合键),而不是使用“合成”键。出于技术原因,合成键的性能可能更好,但是我自己的默认选择是指定并使用真实术语作为主键,除非您确实需要采取其他方式来使服务正常工作。

Microsoft SQL Server具有“聚集索引”的独特但相关的功能,该功能按索引顺序控制数据的物理存储,并且还可以在其他索引中使用。默认情况下,主键被创建为聚集索引,但是您可以选择非聚集键,最好在创建聚集索引之后。因此,您可以将整数标识生成的列作为聚簇索引,并且将文件名nvarchar(128个字符)作为主键。这可能会更好,因为即使您将文件名作为外键项存储在其他表中,聚集索引键也很窄-尽管此示例也是不这样做的一个好例子。

如果您的设计涉及到导入包含不方便的主键来标识相关数据的数据表,那么您就此陷入困境。

https://www.techopedia.com/definition/5547/primary-key描述了一个示例,该示例选择是在所有数据表中存储以客户的社会保险号作为客户密钥的数据,还是在生成数据时生成任意的customer_id注册他们。实际上,除了是否有效,这是对SSN的严重滥用。它是个人和机密数据值。

因此,使用真实事实作为键的一个优点是,无需联接回到“客户”表,您就可以在其他表中检索有关它们的信息-但这也是数据安全性问题。

另外,如果SSN或其他数据密钥的记录不正确,也会给您带来麻烦,因此您在20个约束表中而不是仅在“ Customer”中具有错误的值。合成的customer_id没有外部含义,因此它不会是错误的值。


1
我特别赞赏这种观察,即如果需要纠正数据,则以客户数据为关键,甚至已知的唯一客户数据(此处为SSN)也会崩溃。
ToolmakerSteve
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.