复合主键是不好的做法吗?

hac*_*van 29 database-design

我想知道复合主键是否是不好的做法,如果不是,在哪些情况下使用它们是有益的?

我的问题是基于这篇文章

数据库设计错误

注意关于复合主键的部分:

不良做法 6:复合主键

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

复合主键的图像

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

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

Vér*_*ace 47

要说使用"Composite keys as PRIMARY KEY is bad practice"完全是无稽之谈!

Composite PRIMARY KEYs 通常是一个非常“好东西”,并且是对日常生活中发生的自然情况进行建模的唯一方法!话虽如此,在很多情况下,使用复合 PK 会很麻烦且笨拙,因此不是最佳选择。

你的问题是:(已"if composite primary keys are bad practice...回答)and if not, in which scenarios is their use beneficial?"

下面是一个示例,其中复合键代表作为 PK 的理性/有益选择(确实是我所看到的唯一理性选择 - 在此处的小提琴中,还有一个额外的成绩示例!

在复合键的有利方面,想想经典的 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)
);
Run Code Online (Sandbox Code Playgroud)

我将为您提供PostgreSQL 方言(和MySQL)中的示例- 应该适用于任何稍作调整的服务器。

现在,你显然希望跟踪哪个学生正在这当然-让你拥有什么叫做joining table(也称linkingbridgingmany-to-manym-to-n表)。它们associative entities在技​​术术语中也被称为!

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

所以,你创建了一个连接表

CREATE TABLE registration
(
  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)
);
Run Code Online (Sandbox Code Playgroud)

现在,明智地给出表格 a的唯一方法是将课程和学生结合起来。这样,您将无法获得:registrationPRIMARY KEYKEY

  • 学生和课程组合的副本

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

  • 一个学生只能报读同一门课程一次

  • 您还可以KEY对每个学生的课程进行现成搜索- AKA覆盖索引

  • 找到没有学生的课程和没有上课的学生是微不足道的!

    -- db-fiddle示例将 PK 约束折叠到CREATE TABLE -- 它可以通过任何一种方式完成。我更喜欢在CREATE TABLE声明中包含所有内容。


ALTER TABLE registration
ADD CONSTRAINT registration_pk 
PRIMARY KEY (cs_course_id, cs_student_id);
Run Code Online (Sandbox Code Playgroud)

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

ALTER TABLE registration 
ADD CONSTRAINT course_student_sc_uq  
UNIQUE (cs_student_id, cs_course_id);
Run Code Online (Sandbox Code Playgroud)

没有灵丹妙药添加索引-他们作出INSERTS和UPDATEs ^慢,但在大大大大有利于减少SELECT次!由开发人员根据他们的知识和经验决定索引,但说复合PRIMARY KEYs总是不好的完全是错误的。

在连接表的情况下,它们通常是唯一 PRIMARY KEY有意义的!连接表也经常是对业务或自然或我能想到的几乎所有领域中发生的事情进行建模的唯一方法!

此 PK 还covering index可用作有助于加快搜索速度的PK 。在这种情况下,如果人们经常搜索 (course_id, student_id),这将特别有用,人们可以想象,情况往往如此!

这只是一个小例子,说明复合材料PRIMARY KEY可以是一个非常好的想法,也是模拟现实的唯一明智方法!关闭我的头顶,我能想到的很多很多了。

我自己工作的一个例子!

考虑一个包含 Flight_id、出发和到达机场列表以及相关时间的航班表,然后还有一个包含机组成员的cabin_crew 表!

可以对此建模的唯一明智的方法是拥有一个带有 flight_id 和crew_id 作为属性的 flight_crew 表,唯一合理的PRIMARY KEY是使用这两个字段的复合键!


Rob*_*gie 7

我半途而废的看法:“主键”不必是用于在表中查找数据的唯一唯一键,尽管数据管理工具会将其作为默认选择提供。因此,要选择是将两列的组合还是随机(可能是序列)生成的数字作为表键,您可以同时使用两个不同的键。

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

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

如果您的设计涉及导入包含不方便的主键来识别相关数据的数据表,那么您几乎会陷入困境。

https://www.techopedia.com/definition/5547/primary-key描述了一个例子,在所有数据表中选择是否以客户的社会保险号作为客户密钥存储数据,或者在您注册他们。实际上,这是对 SSN 的严重滥用,不管它是否有效;它是个人和机密的数据价值。

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

此外,如果 SSN 或其他数据键记录不正确,您就会遇到麻烦,因此您在 20 个受约束的表中而不是仅在“客户”中具有错误的值。而合成的 customer_id 没有外部意义,所以它不能是错误的值。