考虑一个专业,比如经济学。假设学生需要选择“EC 101”或“EC 102”,但不一定要同时从该专业毕业。目前,我有两个这样的表:
课程:CourseID、CourseName
专业:MajorID、MajorName、RequiredCourseID
我如何捕捉有时专业的要求是:在我的数据库设计中要么参加课程 A 或课程 B,但不一定同时参加?
考虑一个专业,比如经济学。假设学生需要选择“EC 101”或“EC 102”,但不一定要同时从该专业毕业。目前,我有两个这样的表:
课程:CourseID、CourseName
专业:MajorID、MajorName、RequiredCourseID
我如何捕捉有时专业的要求是:在我的数据库设计中要么参加课程 A 或课程 B,但不一定同时参加?
你需要重构你的表,并引入一些额外的:
课程:CourseID、CourseName
专业:MajorID、MajorName
MajorRequirements:MajorID、ReqId
要求:ReqId、ReqCount
必选课程选项:ReqId、CourseID
从问题:
经济学(EC001):假设学生需要选择“EC 101”或“EC 102”,但不一定要从该专业毕业。
其他要求:
此外,主修 EC001 的学生必须修读所有三门课程 EC 200、EC 201、EC 202。
政治经济学(EC002):与经济学一样,学生需要选修“EC 101”或“EC 102”,但不一定同时选修。此外,主修 EC002 的学生必须选修 EC 200、EC 201、EC 202 三门课程中的任意两门。(而且,可能还有这里未讨论的其他课程。)
Course ID Name
EC 101 Economics 101
EC 102 Economics 102
EC 200 Economics 200
EC 201 Economics 201
EC 202 Economics 202
Major ID Name
EC001 Economics
EC002 Political Economy
MajorID ReqID
EC001 R01
EC001 R02
EC002 R01
EC002 R03
ReqID ReqCount
R01 1
R02 3
R03 2
ReqID CourseID
1 EC 101
1 EC 102
2 EC 200
2 EC 201
2 EC 202
3 EC 200
3 EC 201
3 EC 202
主修经济学(EC001)的人必须满足其专业的所有要求,这意味着必须满足MajorRequirements R01和R02。要满足 R01,学生必须从可用选项中选修 1 门必修课程。必修课程选项为 EC 101 和 EC 102;任何一个都足够了。要满足 R02,学生必须从可用选项中选修 3 门必修课;有三门课程(EC 200、EC 201、EC 202),因此学生必须参加所有三门课程。
同样,主修政治经济学 (EC002) 的人必须满足其专业的所有要求,这意味着必须满足 MajorRequirments R01 和 R03。和以前一样,要达到 R01,学生必须从可用选项(EC 101 或 EC 102)中选修 1 门必修课程。要达到 R03,学生必须从可用选项中选修 2 门必修课;共有三门课程(EC 200、EC 201、EC 202),学生必须至少修读三门课程中的两门。
显然,这可用于要求任何集合中的任何 M 个课程中的任何 N 个。如果主修 M 需要特定课程 C,则 MajorRequirements 表包含主修 M 的 ReqID R,ReqCount 为 1,RequiredCourseOptions 记录 R 和 C。 ReqID 值和 ReqCount 各为 1。但是,我想展示 R03 的 3 门课程中的 2 门的灵活性,并且对称性表明 ReqID R02 与 ReqCount 3 在某些方面更好。
哪些学生有资格毕业?
假设一个包含 StudentID、Name 和 MajorID 列(以及其他列,例如出生日期、入学日期等)的学生表,以及另一个包含 StudentID 和 CourseID 列的 StudentPassedCourses(以及通过日期和通过成绩等)。条目仅在学生通过课程后出现在 StudentPassedCourses 中。
那么有资格毕业的学生就是那些满足了他们专业的每一项要求的学生。
让我们使用TDQD(测试驱动查询设计)逐步构建查询。
SELECT MajorID, COUNT(ReqID) AS CountReqs
FROM MajorRequirements
GROUP BY MajorID
SELECT s.StudentID, m.ReqID, COUNT(*) AS PassCount
FROM StudentPassedCourses AS p
JOIN Students AS s ON p.StudentID = s.StudentID
JOIN MajorRequirements AS m ON s.MajorID = m.MajorID
GROUP BY s.StudentID, m.ReqID
(这是一个相当大的步骤;它可能需要分解为单独的步骤。)
这列出了学生和要求 ID,其中给定 ReqID 的学生通过计数至少是专业要求的通过计数。
SELECT p.StudentID, p.ReqID
FROM (SELECT s.StudentID, m.ReqID, COUNT(*) AS PassCount -- Q2
FROM StudentPassedCourses AS p
JOIN Students AS s ON p.StudentID = s.StudentID
JOIN MajorRequirements AS m ON s.MajorID = m.MajorID
GROUP BY s.StudentID, m.ReqID
) AS p
JOIN MajorRequirements AS m ON p.ReqID = m.ReqID
WHERE p.PassCount >= m.ReqCount
SELECT r.StudentID, COUNT(*) AS ReqsPassed
FROM (SELECT p.StudentID, p.ReqID -- Q3
FROM (SELECT s.StudentID, m.ReqID, COUNT(*) AS PassCount -- Q2
FROM StudentPassedCourses AS p
JOIN Students AS s ON p.StudentID = s.StudentID
JOIN MajorRequirements AS m ON s.MajorID = m.MajorID
GROUP BY s.StudentID, m.ReqID
) AS p
JOIN MajorRequirements AS m ON p.ReqID = m.ReqID
WHERE p.PassCount >= m.ReqCount
) AS r
GROUP BY r.StudentID
SELECT s.StudentID, s.Name, s.MajorID, m.Name Major
FROM Students AS s
JOIN Majors AS m ON m.MajorID = s.MajorID
JOIN (SELECT r.StudentID, COUNT(*) AS ReqsPassed -- Q4
FROM (SELECT p.StudentID, p.ReqID -- Q3
FROM (SELECT s.StudentID, m.ReqID, COUNT(*) AS PassCount -- Q2
FROM StudentPassedCourses AS p
JOIN Students AS s ON p.StudentID = s.StudentID
JOIN MajorRequirements AS m ON s.MajorID = m.MajorID
GROUP BY s.StudentID, m.ReqID
) AS p
JOIN MajorRequirements AS m ON p.ReqID = m.ReqID
WHERE p.PassCount >= m.ReqCount
) AS r
GROUP BY r.StudentID
) AS c ON c.StudentID = s.StudentID
JOIN (SELECT MajorID, COUNT(ReqID) AS CountReqs -- Q1
FROM MajorRequirements
GROUP BY MajorID
) AS r ON r.MajorID = s.MajorID
WHERE c.ReqsPassed >= r.CountReqs
警告:未经测试的 SQL!
一个专业有要求,一个要求可以通过课程来填补。这样,专业也可以分享共同的要求。
major requirementId
-----------------------
econ 1
econ 2
artOrSmth 2
requirementId coursename
------------------------------
1 econ101
1 econ102
2 math101