RDB 中的子关系
Subrelation in RDB
在典型的 RDB 中,我可以描述与外键的关系。但是,我不确定如何有效地描述组内关系。我知道这很难理解,所以让我描述一下这个场景。
我想做一个成绩簿服务,教师可以在其中注册和创建 classes,学生可以在 classes 中注册和注册。
CREATE TABLE teachers (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255),
...
)
CREATE TABLE students (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255),
...
)
CREATE TABLE classes (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255),
teacher_id BIGINT REFERENCES teachers(id)
)
CREATE TABLE enrollments (
student_id BIGINT REFERENCES students(id),
class_id BIGINT REFERENCES classes(id),
PRIMARY KEY (student_id, class_id)
)
在每个 class 中,教师可以创建作业,每个学生都会收到作业的分数。
CREATE TABLE assignments (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255),
class_id BIGINT REFERENCES class(id)
...
)
现在,我将描述一个属于学生的分数,以及一个带有外键的作业。
CREATE TABLE scores (
id BIGSERIAL PRIMARY KEY,
score INT,
student_id BIGINT REFERENCES student(id),
assignment_id BIGINT REFERENCES assignments(id)
)
但是,没有什么可以阻止我为没有参加此活动的学生创建分数 class。换句话说,分数必须伴随着相应的注册(相同的学生 ID)。我说这是子关系的原因是因为 score 的关系应该在 class.
的局部范围内
如何使用 RDB 强制实施此类限制?
更新:
根据@Joel Brown 的建议,这是我想出的。
CREATE TABLE enrollments (
class_id BIGINT REFERENCES classes(id),
student_id BIGINT REFERENCES students(id),
PRIMARY KEY (class_id, student_id)
);
CREATE TABLE assignments (
id BIGSERIAL,
class_id BIGINT REFERENCES classes(id),
name VARCHAR(255) NOT NULL,
max_possible_score INT NOT NULL,
PRIMARY KEY (id, class_id)
);
CREATE TABLE scores (
class_id BIGINT,
student_id BIGINT,
assignment_id BIGINT,
PRIMARY KEY (assignment_id, class_id, student_id),
FOREIGN KEY (class_id, student_id) REFERENCES enrollments(class_id, student_id),
FOREIGN KEY (assignment_id, class_id) REFERENCES assignments(id, class_id)
);
如果您想确保学生无法在他们未注册的 class 的作业中获得 SCORE
,您有两个选择:
- 使用应用程序逻辑
- 使用声明性参照完整性
有些人可能会争辩说您现在拥有的数据模型工作得很好,但它需要一些应用程序逻辑来对 SCORE
的创建执行健全性检查,以确保学生应该拥有得分.
如果要确保在不使用应用程序逻辑的情况下不会发生这种情况,则必须更改数据模型。您当前的数据模型如下所示:
您可以将数据模型更改为如下所示:
注意以下几点:
-
Assignment
部分由其指向 CLASS
的外键标识。这会将 Class ID 下拉到分配中,以便 SCORE
在其主键中依次引用它。
SCORE
与 ENROLLMENT
直接相关,而不是与 STUDENT
直接相关。这是有道理的,因为注册是属于 class 的学生的记录 - 因此它更接近地模拟您所关心的实施的业务规则。
SCORE
的主键由 ENROLLMENT
(包括学生 ID)和 ASSIGNMENT
(包括 Class ID)的外键组成。
- 这意味着
SCORE
的主键将是 class、assignment 和 student 的组合。
如果您确保在 SCORE
table 中只保留一份 class ID,那么学生将无法在一项作业,除非该学生也注册了分配该作业的 class。
在典型的 RDB 中,我可以描述与外键的关系。但是,我不确定如何有效地描述组内关系。我知道这很难理解,所以让我描述一下这个场景。
我想做一个成绩簿服务,教师可以在其中注册和创建 classes,学生可以在 classes 中注册和注册。
CREATE TABLE teachers (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255),
...
)
CREATE TABLE students (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255),
...
)
CREATE TABLE classes (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255),
teacher_id BIGINT REFERENCES teachers(id)
)
CREATE TABLE enrollments (
student_id BIGINT REFERENCES students(id),
class_id BIGINT REFERENCES classes(id),
PRIMARY KEY (student_id, class_id)
)
在每个 class 中,教师可以创建作业,每个学生都会收到作业的分数。
CREATE TABLE assignments (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255),
class_id BIGINT REFERENCES class(id)
...
)
现在,我将描述一个属于学生的分数,以及一个带有外键的作业。
CREATE TABLE scores (
id BIGSERIAL PRIMARY KEY,
score INT,
student_id BIGINT REFERENCES student(id),
assignment_id BIGINT REFERENCES assignments(id)
)
但是,没有什么可以阻止我为没有参加此活动的学生创建分数 class。换句话说,分数必须伴随着相应的注册(相同的学生 ID)。我说这是子关系的原因是因为 score 的关系应该在 class.
的局部范围内如何使用 RDB 强制实施此类限制?
更新:
根据@Joel Brown 的建议,这是我想出的。
CREATE TABLE enrollments (
class_id BIGINT REFERENCES classes(id),
student_id BIGINT REFERENCES students(id),
PRIMARY KEY (class_id, student_id)
);
CREATE TABLE assignments (
id BIGSERIAL,
class_id BIGINT REFERENCES classes(id),
name VARCHAR(255) NOT NULL,
max_possible_score INT NOT NULL,
PRIMARY KEY (id, class_id)
);
CREATE TABLE scores (
class_id BIGINT,
student_id BIGINT,
assignment_id BIGINT,
PRIMARY KEY (assignment_id, class_id, student_id),
FOREIGN KEY (class_id, student_id) REFERENCES enrollments(class_id, student_id),
FOREIGN KEY (assignment_id, class_id) REFERENCES assignments(id, class_id)
);
如果您想确保学生无法在他们未注册的 class 的作业中获得 SCORE
,您有两个选择:
- 使用应用程序逻辑
- 使用声明性参照完整性
有些人可能会争辩说您现在拥有的数据模型工作得很好,但它需要一些应用程序逻辑来对 SCORE
的创建执行健全性检查,以确保学生应该拥有得分.
如果要确保在不使用应用程序逻辑的情况下不会发生这种情况,则必须更改数据模型。您当前的数据模型如下所示:
您可以将数据模型更改为如下所示:
注意以下几点:
-
Assignment
部分由其指向CLASS
的外键标识。这会将 Class ID 下拉到分配中,以便SCORE
在其主键中依次引用它。 SCORE
与ENROLLMENT
直接相关,而不是与STUDENT
直接相关。这是有道理的,因为注册是属于 class 的学生的记录 - 因此它更接近地模拟您所关心的实施的业务规则。SCORE
的主键由ENROLLMENT
(包括学生 ID)和ASSIGNMENT
(包括 Class ID)的外键组成。- 这意味着
SCORE
的主键将是 class、assignment 和 student 的组合。
如果您确保在 SCORE
table 中只保留一份 class ID,那么学生将无法在一项作业,除非该学生也注册了分配该作业的 class。