数据库设计 - 多个外键

Posted

技术标签:

【中文标题】数据库设计 - 多个外键【英文标题】:Database design - multiple foreign keys 【发布时间】:2019-10-09 22:30:58 【问题描述】:

我的数据库包含学校、部门和课程。学校可以有分部,也可以没有,课程跟学校有关系,也可以不和分部有关(学校可以没有分部,也可以跨分部)

我的桌子目前设置如下:

school:

ID       | name
--------------------
harvard  | Harvard University
mit      | MIT
ucla     | UCLA

division (id+school=unique)

ID  | school (FK) | name
------------------------------------------
eng | harvard     | School of Engineering
arc | harvard     | School of Architecture
eng | UCLA        | UCLA Engineering

course:

ID | school (FK) | division | name
-------------------------------------------------
1  | harvard     | eng      | Intro to Engineering 
2  | harvard     | arc      | Intro to Architecture
3  | harvard     |          | Statistics
4  | mit         |          | Math

我对此的担忧:

没有验证以确保course 中的部门存在并且与学校相关。 除法实际上不是 fk 需要两次查询才能得到学校和部门

有没有更好的方法来做到这一点?我希望能够:

查询所有“哈佛”课程 查询所有“哈佛工程”课程 查询所有“哈佛工程与哈佛通用”课程

【问题讨论】:

【参考方案1】:

您可以创建一个多列外键:

CREATE TABLE course (
    id INT(11) AUTO_INCREMENT PRIMARY KEY,
    school VARCHAR(50),
    division VARCHAR(50),
    name VARCHAR(50),
    FOREIGN KEY (school, division) REFERENCES division(school, id)
);

但是,在division 表中使用单独的AUTO_INCREMENT 列并将其用作外键可能会更好。这样您就不必在 course 表中复制两列。

CREATE TABLE division (
    id INT(11) AUTO_INCREMENT PRIMARY KEY,
    division_code VARCHAR(50),
    school VARCHAR(50),
    name VARCHAR(50),
    UNIQUE KEY (division_code, school),
    FOREIGN KEY (school) REFERENCES school (id)
);
CREATE TABLE course (
    id INT(11) AUTO_INCREMENT PRIMARY KEY,
    division_id INT(11),
    name VARCHAR(50),
    FOREIGN KEY (division_id) REFERENCES division(id)
);

【讨论】:

【参考方案2】:

如果总是可以创建复合外键。但是,我建议对您的设计进行以下更改:

为每所学校创建一个默认部门,称为“General” 允许同一课程存在于不同学校的可能性(在现实生活中,这可能会发生)。

我还建议对所有表使用自动递增的整数主键,而不是依赖手动构建的名称。

考虑以下设计,它遵循上述原则:

school
    id            primary key
    name

division
    id            primary key
    school_id     foreign key to school(id)
    name

course
    id            primary key
    name

course_division
    id            primary key
    course_id     foreign key to course(id)
    division_id   foreign key to course(id)

现在这里是使用此架构的查询。

查询所有“哈佛”课程

SELECT c.* 
FROM course c
INNER JOIN division d ON d.id = cd.division_id
INNER JOIN school s ON s.id = d.school_id AND s.name = 'Harvard University'

查询所有“哈佛工程”课程

SELECT c.* 
FROM course c
INNER JOIN course_division cd ON cd.id = c.course_id 
INNER JOIN division d ON d.id = cd.division_id AND d.name = 'School of Engineering'
INNER JOIN school s ON s.id = d.school_id AND s.name = 'Harvard University'

查询所有“哈佛工程与哈佛通识”课程

SELECT c.* 
FROM course c
INNER JOIN course_division cd ON cd.id = c.course_id 
INNER JOIN division d ON d.id = cd.division_id AND d.name IN ('School of Engineering', 'General')
INNER JOIN school s ON s.id = d.school_id AND s.name = 'Harvard University'

【讨论】:

以上是关于数据库设计 - 多个外键的主要内容,如果未能解决你的问题,请参考以下文章

数据库中主键和外键的作用?

映射到多个主键的外键列

如何确定哪些列用作外键?

SQLAlchemy:表有多个外键约束关系

使用多个外键级联删除

数据库设计可以不要外键的7个理由