连接表的代理键在限制与外键的更深关系时有一个巨大的缺点。我们需要一个 6 表设置来演示它。
基表:
CREATE TABLE semester (semester_id INTEGER PRIMARY KEY, semester_name VARCHAR(40));
CREATE TABLE student (student_id INTEGER PRIMARY KEY, student_name VARCHAR(40));
CREATE TABLE subject (subject_id INTEGER PRIMARY KEY, subject_name VARCHAR(40));
然后让我们连接它们:
CREATE TABLE enrollment (
enrollment_id INTEGER PRIMARY KEY,
semester_id INTEGER NOT NULL,
student_id INTEGER NOT NULL,
room_number INTEGER,
FOREIGN KEY (semester_id) REFERENCES semester (semester_id),
FOREIGN KEY (student_id) REFERENCES student (student_id),
UNIQUE INDEX (semester_id, student_id)
);
-- similarly ...
CREATE TABLE class(class_id ..., semester_id ..., subject_id ..., class_number ...);
到目前为止,一切都很棒。但是,我们需要更多地连接它们:
CREATE TABLE grades (
student_in_class_id INTEGER PRIMARY KEY,
enrollment_id INTEGER NOT NULL,
class_id INTEGER NOT NULL,
grade char(1),
FOREIGN KEY enrollment (enrollment_id),
FOREIGN KEY class (class_id),
UNIQUE INDEX (enrollment_id, class_id)
);
问题: 我们到底应该如何强制注册和班级都参考同一个学期?(简短回答:我们可以't)
复合键也是如此——在这里我不需要额外的键来强制执行外键的唯一组合,主键默认为我执行此操作:
CREATE TABLE enrollment (
semester_id INTEGER NOT NULL,
student_id INTEGER NOT NULL,
room_number INTEGER,
PRIMARY KEY (semester_id, student_id),
FOREIGN KEY (semester_id) REFERENCES semester (semester_id),
FOREIGN KEY (student_id) REFERENCES student (student_id)
);
-- along the same lines...
class(semester_id ..., subject_id ..., class_number ...)
然后是不祥等级表:
CREATE TABLE grades (
semester_id INTEGER NOT NULL,
student_id INTEGER NOT NULL,
subject_id INTEGER NOT NULL,
PRIMARY KEY (semester_id, student_id, subject_id),
FOREIGN KEY (semester_id, student_id) REFERENCES enrollment(semester_id, student_id),
FOREIGN KEY (semester_id, subject_id) REFERENCES class(semester_id, subject_id)
);
这样,我的关系就可以正确地表达和约束。
额外奖励:我可以通过简单的连接获得我的semester_name、student_name 和subject_name 值(而不必遍历多个级别的架构。)您可能会比这个最简单的例子有更多的层次,然后就更加明显地回到任何父关系是多么容易。
改变架构并不更复杂,但即使是这样:数据建模难道不是关于建模数据吗?我们可以仅仅因为我们喜欢代理键就抛弃参照完整性吗?
注意:Hibernate确实处理复合键。 DataMapper for Ruby 也是如此。