我想知道复合主键是否是不好的实践,如果不是,在哪些情况下它们的使用是有益的?
我的问题是基于这个文章

请注意关于复合主键的部分:
错误实践第6号:复合主键--这是一个有争议的问题,因为现在许多数据库设计人员谈论使用一个整数ID自动生成的字段作为主键,而不是一个由两个或多个字段组合定义的复合键。目前,这被定义为“最佳做法”,就我个人而言,我倾向于同意这种做法。

然而,这只是一个约定,当然,DBE允许定义复合主键,许多设计人员认为这是不可避免的。因此,与冗余一样,组合主键是一个设计决策。不过,请注意,如果您的表中包含一个复合主键,那么控制复合键的索引可能会增长到CRUD操作性能严重下降的地步。在这种情况下,最好使用一个简单的整数ID主键,其索引将足够紧凑,并建立必要的DBE约束以保持唯一性。
发布于 2017-10-21 15:22:09
说"Composite keys as PRIMARY KEY is bad practice"的使用完全是胡说八道!
复合PRIMARY KEYs常常是一件非常“好的事情”,也是模拟日常生活中发生的自然情况的唯一方法!话虽如此,也会有许多情况下,使用复合PK将是繁琐和笨重,因此不是一个最佳选择。
你的问题是:"if composite primary keys are bad practice... (回答) and if not, in which scenarios is their use beneficial?"。
下面是一个例子,其中复合键代表了一个合理的/有益的选择作为PK (实际上,唯一的理性的一个,在我看来-在小提琴这里,有一个额外的例子,也有分数!
在组合键的优点方面,想想经典的数据库--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 (也称为linking、bridging、many-to-many或m-to-n表)。他们也被称为associative entities在更多的技术术语!
一门课程可以有很多学生。
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)
);现在,明智地给registration表一个PRIMARY KEY的唯一方法是使KEY成为一个当然和学生的组合。那样的话,你就不能得到:
KEY - AKA a 覆盖指数,CREATE TABLE中--它可以任意一种方式完成。我更喜欢CREATE TABLE语句中的所有内容。ALTER TABLE registration
ADD CONSTRAINT registration_pk
PRIMARY KEY (cs_course_id, cs_student_id);现在,如果您发现按课程搜索学生的速度很慢,可以使用UNIQUE INDEX on (sc_student_id,sc_course_id)。
ALTER TABLE registration
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,一个离港和到达机场的列表以及相关的时间,然后是一个包含机组人员的cabin_crew表!
唯一合理的建模方法是使用flight_crew表,flight_id和crew_id作为参数,而唯一理智的PRIMARY KEY是使用这两个字段的复合键!
发布于 2017-10-22 11:55:48
我半学识的观点是:“主键”不一定是唯一用于在表中查找数据的键,尽管数据管理工具将提供它作为默认选择。因此,为了选择是两个列的组合还是一个随机(可能是序列)生成的数字作为表键,您可以同时拥有两个不同的键。
如果数据值包含一个可以表示行的合适的唯一术语,我宁愿声明它为“主键”,即使是复合键,也不愿使用“合成”键。由于技术原因,合成密钥可能表现得更好,但我自己的默认选择是指定并使用实际术语作为主键,除非您确实需要采用另一种方式来使您的服务工作。
Microsoft SQL Server具有“聚集索引”的独特但相关的特性,它按索引顺序控制数据的物理存储,并在其他索引中使用。默认情况下,主键作为聚集索引创建,但您可以选择非聚集索引,最好是在创建聚集索引之后。因此,可以将生成的整数标识列作为聚集索引,例如,文件名nvarchar(128个字符)作为主键。这可能更好,因为聚集索引键很窄,即使将文件名作为外键项存储在其他表中也是一个很好的例子。
如果您的设计涉及导入包含一个不方便的主键来识别相关数据的数据表,那么您就会很难做到这一点。
https://www.techopedia.com/definition/5547/primary-key描述了选择在所有数据表中存储以客户的社会保险号码作为客户密钥的数据,还是在注册数据时生成任意customer_id的示例。事实上,这是对SSN的严重滥用,不管它是否有效;它是一种个人和机密的数据价值。
因此,使用现实世界的事实作为关键的好处是,在不加入"Customer“表的情况下,您可以在其他表中检索有关它们的信息--但这也是一个数据安全问题。
此外,如果SSN或其他数据键被错误地记录,那么就会遇到麻烦,因此在20个受约束的表中有错误的值,而不是仅在"Customer“中。然而合成的customer_id没有外部意义,所以它不会是一个错误的值。
发布于 2022-06-17 14:38:35
就@Vérace-СлаваУкраїні给出的答案进行详细阐述。当您想要深入到2级以上时,也需要复合键。如果我们继续给出的例子,每门课程都可能有作业。
Create Table Assignment {
assignmentid int not null,
assignment title varchar(255) not null,
assignment details text null,
deadline datetime null,
constraint pk_assignment Primary Key (assignmentid)
}但是,如果没有课程的上下文,这是行不通的,所以我们可以向表中添加一个课程id
Alter Table Assignment add courseid int not null但现在你想看学生的意见书。这意味着我们需要一个表链接到注册的外派。
CREATE TABLE student_assignment
(
cs_course_id INTEGER NOT NULL,
cs_student_id INTEGER NOT NULL,
cs_assignment_id integer not null,
grade varchar(32) null,
assignment blob null,
submittedtime datetime null,
Constraint pk_student_assignment primary key (cs_course_id, cs_student_id, cs_assignment_id)
}正如您所看到的,这个表的自然键是所有三列。您可以只使用作业id和学生id,将其降到两列,这是因为课程id已经存在于赋值表中。然而,这样做更难做出有意义的报告。例如,一门课程有多少学生迟交作业,成绩分布如何等。
https://dba.stackexchange.com/questions/188995
复制相似问题