如何使复合键唯一?

Posted

技术标签:

【中文标题】如何使复合键唯一?【英文标题】:How to make a composite key to be unique? 【发布时间】:2012-08-02 08:19:31 【问题描述】:

我正在为一所学校的学生创建一个数据库。这是我目前所拥有的:

如果您不喜欢阅读,请跳至“简而言之”部分

问题是我对这个设计不满意。我希望gradesubgradeid_class 的组合是唯一的,并作为学生表的主键。 我可以删除 student_id 并从 3 中创建一个复合键,但我也不想要这样。也许我应该创建另一个表,比如说combination_id,其中gradesubgradeid_class 是外键,还有一个额外的列comb_id 作为表的ID。所有的列都将是主键。但问题是这 3 列仍然可以重复,因为那个额外的列 (comb_id)。例如,我可以拥有相同的 gradesubgradeclass_id,但不同的 comb_id 由于表的 4 列 (combination_id) 的复合键,这将使行有效。

简而言之,我希望 students_id 保持表的唯一主键,但作为另一个表的外键,该表是 gradessubgradeclass_id 的独特组合。

如果我不够清楚,请在下面的 cmets 中提问,并提前感谢您。

PS 我很抱歉这个难以描述的标题,但我不擅长命名

编辑 1: 为了更清楚: grade 可以是 1 到 12 subgrade 可以是 a 到 j id_class 可以是 1 到 30,是你的班级号码

所以一个学生可以来自 7b 班级,他的班级人数 - 5

【问题讨论】:

我不明白,一个班的所有学生都应该有不同的成绩吗? 当学生换班时你会怎么做,或者这种情况永远不会发生? student_id 列有什么问题? 学生是否只有一个班级,如小学/初中/幼儿学校? @Jodrell 我已经编辑了我的问题,以便更清楚地了解这些列。我希望 student_id 成为另一个表中的 FK,其中包含用作其中复合键的那 3 列。如果您仍有问题,请询问 【参考方案1】:

不要混淆唯一键主键的概念。您可以很好地添加一个unique key 跨越三列gradessubgradeclass_id。这样一来,没有两行could have 这三列的值相同。当您写到您不想将这三个作为复合主键时,我不确定复合唯一补充键是否会更好。如果不是,您必须说明何时可以接受复合键。

要创建唯一键,可以使用以下 SQL statement:

ALTER TABLE students ADD UNIQUE gsc (grades, subgrade, class_id);

gsc这个词只是我从关键列的首字母组成的一个名字;使用您想要的任何名称,因为它几乎不重要,除非您想在某些 EXPLAIN 输出或类似输出中识别密钥。

【讨论】:

谢谢,我会试试的,我可能会将人们与 gradesubgradeid_class 的名称混淆,但在我的国家,教育系统是不同的。 好的,我试过了,但是当我创建了所有 3 列 UNIQUE 时,我收到了重复条目的错误。我有很多 grades 3 但它们有不同的 subgrades 所以它将它们视为单独的 unique 键。还有什么是补充? 不要使所有 thre 列本身唯一,而是一起使:ALTER TABLE students ADD UNIQUE gsc (grades, subgrade, class_id)gsc 只是一个名字,随便你怎么用。然后,只有三个都相同时才会出现重复。我使用了 supplementary 这个术语而不是 primary:使用此命令定义的键不是主键,但仍然是唯一的。【参考方案2】:

我不完全清楚你为什么想要你所描述的,但我会以以下方式查看模型......

你有学生 - 它们是不同的实体,而不是其他实体的组合 - 他们有自己的属性;姓名、出生日期等

你有课程 - 这些是学生群体 - 每个学年同一个“班级”有不同的学生在里面 - 他们也有自己的属性;等级、次等级等

您的模型中有一个我通常不会使用的额外属性 - 如果一个班级有 20 名学生,每个学生都使用从 1 到 20 的次要 id 进行标识

这会给以下维度表

Student                  Class                     Grade              SubGrade
-----------------------  ------------------------  -----------------  -----------------
id          INT PK       id           INT PK       id    INT PK       id    INT PK
first_name  VARCHAR(45)  name         VARCHAR(45)  name  VARCHAR(45)  name  VARCHAR(45)
last_name   VARCHAR(45)  grade_id     INT FK       desc  VARCHAR(45)  desc  VARCHAR(45)
etc, etc                 subgrade_id  INT FK       etc, etc           etc, etc

Class 表将对(grade_id, subgrade_id) 具有唯一约束,因此只有一个类可以是7b

然后你需要使用事实表将学生与他们的班级联系起来......

Class_Membership
-----------------------
id               INT PK
student_id       INT FK
class_id         INT FK
academic_year    INT

如果一个学生在任何一个学年都只能上一个班级,那么您可以对 (student_id, academic_year) 设置一个独特的限制条件。

或者,您可以在Class 表中填写学年。这意味着您每年都会重复相同的课程,但在某些年份可能不存在课程7g(例如,因为那一年的学生较少)

同样,您可以让学生在年中从7b 转到7c。在这种情况下,Class_Membership 表可能有一个start_date 字段,也可能有一个end_date 字段。

然而,这些都不是直接创建id_class 字段(1-20 代表一个有 20 名学生的班级)。就我个人而言,我不会有这样的字段,Class_Membership 表中的 id 字段可以提供大部分相同的功能,可能还有其他功能。但是,如果需要,您可以简单地将其添加到Class_Membership 表中...

Class_Membership
-----------------------
id               INT PK
student_id       INT FK
class_id         INT FK
academic_year    INT
class_member_id  INT

那么您还可以对 (academic_year, class_id, class_member_id) 设置唯一约束。

这里有很大的灵活性,具体取决于您的实际模型和您的特定需求。但希望这个例子对你来说是一个好的开始;列出实体的维度表,以及将这些实体关联在一起和/或进一步描述实体的事实表(或多个表)。

【讨论】:

当您询问当前的模式时,我将在这里添加我的想法。 @Bosak,在我的理解中,“路基”只是一个区分同一年级不同班级的字母。因此,虽然等级和路基标识了一个类,但路基本身并不是一个真正的实体。您很少会对“所有具有路基 b 的类”感兴趣,甚至对存储所有这些 b 类的公共属性更不感兴趣,对吧?在这种情况下,我会删除 SubGrade 表。【参考方案3】:
alter table <TableName> add constraint <ConstraintName> unique(<col1>, <col2>)

Modified the answer due to syntax mistakes. Mentioned above is correct.

【讨论】:

以上是关于如何使复合键唯一?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Django 的复合/复合主/唯一键

如何使用 JSF 复合组件使页面上的 id 唯一?

如何在 Sequelize 中使用外键和常规键创建复合唯一约束?

教义和复合唯一键

SQL Server + 复合键或唯一 ID

复合可分解唯一键