我有这样的数据库布局。
Prerequisite: +---------------+---------------+ | Id | Name | (Junction table) |---------------|---------------| CoursePrerequisites: | 1 | Maths | +---------------+---------------+ | 2 | English | | Course_FK | Prerequisite_FK | 3 | Art | |---------------|---------------| | 4 | Physics | | 1 | 1 | | 5 | Psychology | | 1 | 2 | +-------------------------------+ | 2 | 3 | | 2 | 5 | Course: | 5 | 4 | +---------------+---------------+ +---------------v---------------+ | Id | Name | |---------------|---------------| | 1 | Course1 | | 2 | Course2 | | 3 | Course3 | | 4 | Course4 | | 5 | Course5 | +---------------v---------------+
并且我一直在使用以下查询:
SELECT Course.id, course.Name, GROUP_CONCAT(DISTINCT Prerequisite.Name) AS 'Prerequisite Name(s)' FROM Course LEFT JOIN CoursePrerequisites ON Course.id = CoursePrerequisites.Course_FK LEFT JOIN Prerequisite ON Prerequisite.id = CoursePrerequisites.Prerequisite_FK WHERE NOT EXISTS (SELECT 1 FROM CoursePrerequisites WHERE Course.id = CoursePrerequisites.Course_FK AND CoursePrerequisites.Prerequisite_FK NOT IN (SELECT Prerequisite.id FROM Prerequisite Where Name = 'Art' OR Name = 'English' OR Name = 'Psychology'')) GROUP BY Course.id;
选择适合其先决条件的课程非常有效。
但是,我遇到了一个障碍,试图以一种能够代表具有复合先决条件的课程的方式来组织数据库。例如,一门课程可能需要英语,数学以及艺术或心理学。另一个示例可能是先决条件英语,以及物理,心理学,艺术等两者中的两个。
什么是构造数据库以处理这些类型的先决条件的合适方法(我尝试进行一些搜索,但我什么都没找到以及如何修改上面的查询以仅返回至少满足其先决条件的课程?
为了澄清起见:给定一个主题列表(来自“前提条件”表),我希望返回一个列表,列出符合这些主题的课程。在当前的数据库模式中,给定数学,英语,艺术和物理,返回的课程应为课程1和课程5(而不是课程2-它具有美术和心理学的先决条件,给定输入不满足后者的先决条件),如连接表。我希望将课程先决条件的复杂性从简单的“与”(课程1需要数学和英语)扩展到可以处理“或” /一组y中的x的东西(例如,课程1现在需要英语,数学和一个或更多的艺术或心理学)。
进度编辑:
我一直在考虑用一些额外的列来扩展联结表,这些列用于“以下至少一个”和“至少两个以下”等,以及另一列用于“所有”,并将先决条件放入结构中那样。这是解决问题的明智方法,并且在MySQL中如何有效地查询以给定主题列表查找符合条件的课程?
进步:
Kuba Wyrostek在下面建议将每个课程的所有先决条件组合列举为不同的集合。尽管这行得通,但我需要对大约6000个行进行此操作,每个行都有许多枚举。有没有更有效的方法来做到这一点?
我认为在一个表中对合取和合取建模总是很不容易,并且会导致违反正常形式或无法预测需要多少个自我联接。我了解的是,您的前提条件通常可以表达为连词的替代形式。因此,以下内容:
Math AND English AND (Physics1 OR Physics2)
可能表示为:
(Math AND English AND Physics1) OR (Math AND English AND Physics2)
得出的结论是,您可能需要一个描述 先决条件集的 中间表。当 任何 一组成功时,课程就可用;而当一组中的 所有 科目都完成时,则该课程就成功。
因此结构可能如下所示:
Prerequisite: +---------------+---------------+ | Id | Name | |---------------|---------------| PrerequisiteSets: | 1 | Maths | +---------------+---------------+ | 2 | English | | SetNumber | Prerequisite_FK | 3 | Art | |---------------|---------------| | 4 | Physics | | 1 | 1 | | 5 | Psychology | | 1 | 2 | +-------------------------------+ | 1 | 4 | | 2 | 1 | | 2 | 2 | Course: | 2 | 5 | +---------------+---------------+ +---------------v---------------+ | Id | Name | |---------------|---------------| | 1 | Course1 | | 2 | Course2 | | 3 | Course3 | | 4 | Course4 | | 5 | Course5 | +---------------v---------------+ CoursePrerequisite: +---------------+---------------+ | Course_FK | SetNumber | |---------------|---------------| | 5 | 1 | | 5 | 2 | +---------------v---------------+
示例5可以使用SetNumber 1(数学,英语,物理学)或SetNumber2(数学,英语,心理学)来满足。
不幸的是,现在为时已晚,无法为您提供确切的查询,但是如果您需要,我明天可以扩展我的答案。祝你好运!:-)
编辑
为了生成查询,我将从观察开始,当集合中的所有先决条件都是给定先决条件的子集时,将匹配该特定集合。这导致条件,集合中不同先决条件的数量必须与该集合中的给定集合中的前提条件的数量匹配。基本上(假设SetNumber- Prerequisite_FK是表中的唯一对):
select SetNumber, count(Prerequisite_FK) as NumberOfRequired, sum(case when Prerequisite.Name in ('Math','English','Art') then 1 else 0 end) as NumberOfMatching from PrerequisiteSets inner join Prerequisite on PrerequisiteSets.Prerequisite_FK = Prerequisite.ID group by SetNumber having count(Prerequisite_FK) = sum(case when Prerequisite.Name in ('Math','English','Art') then 1 else 0 end)
现在获取最终课程归结为获取所有课程,在上面的查询结果中至少找到了一组编号。像这样开始(可以更好地表示并通过连接进行优化,但总体思路是相同的):
select Id, Name from Course where Id in (select Course_FK from CoursePrerequisite where SetNumber in ( -- insert query from above (but only first column: SetNumber, skip the two latter) ) as MatchingSets ) as MatchingCourses