在典型的RDB中,我可以描述与外键的关系。但是,我不确定如何有效地描述群体内的关系。我知道这很难理解,所以让我来描述这种情况。
我想制作成绩簿服务,教师可以在这里注册和创建课程,学生可以注册和注册课程。
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)
)
在每个班级中,教师都可以创建作业,每个学生都将获得作业的分数。
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)
)
然而,没有什么可以阻止我为没有参加这门课程的学生创建一个分数。换句话说,分数必须伴随相应的注册(相同的学生ID)。我说这是子关系的原因是因为得分的关系应该在一个类的本地范围内。
如何使用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)
);
答案 0 :(得分:1)
如果您想确保学生无法获得未注册课程的作业SCORE
,您有两种选择:
有些人可能会说你现在拥有的数据模型很好但是需要一些应用程序逻辑来对SCORE
的创建进行健全性检查,以确保学生应该得到那个分数。
如果要确保在不使用应用程序逻辑的情况下不会发生这种情况,则必须更改数据模型。您当前的数据模型如下所示:
请注意以下事项:
Assignment
部分由CLASS
的外键标识。这会将类ID下拉到分配,以便它的主键中的SCORE
依次引用它。SCORE
直接与ENROLLMENT
相关,而不是与STUDENT
直接相关。这是有道理的,因为注册是属于某个班级的学生的记录 - 因此它更贴切地模拟了您关注的业务规则。SCORE
的主键由ENROLLMENT
(包括学生ID)和ASSIGNMENT
(包括班级ID)的外键组成。 SCORE
的主键将是班级,作业和学生的组合。 如果您确保只在SCORE
表格中保留一份班级ID,那么除非该学生也注册,否则学生无法获得作业的分数在给予该任务的班级中。