[英]SQL Query to select subjects that satisfy prerequisite requirements
I have the following tables: 我有以下表格:
COURSE 课程
+----------+-------------------------+
| course_id | course_name |
+-----------+------------------------+
| 1 | s001 |
| 2 | s002 |
| 3 | s003 |
| 4 | s004 |
+-----------+------------------------+
COURSE_PREREQUISITES COURSE_PREREQUISITES
+----------+-------------------------+
| course_id | prerequisite_course_id |
+-----------+------------------------+
| 3 | 2 |
+-----------+------------------------+
| 4 | 1 |
+-----------+------------------------+
| 4 | 2 |
+-----------+------------------------+
| 4 | 3 |
+-----------+------------------------+
My question is: Given a list of Course IDs a student has completed, how can I obtain a list of courses the student is eligible to participate in? 我的问题是:给定学生已完成的课程ID列表,我如何获得该学生有资格参加的课程列表?
Example 例
If a student has completed course_id
2, the query should return courses: 1, (since it has no prerequisites) and 3 but not 4 since 4 has 1,3 as prerequisites as well. 如果学生已完成
course_id
2,则查询应返回课程:1,(因为它没有先决条件)和3,但不是4,因为4也有1,3作为先决条件。
Attempt at a solution 尝试解决方案
I have tried using the IN
statement like so for a student who has completed the course 2: 对于已完成课程2的学生,我已经尝试使用
IN
语句:
SELECT DISTINCT course_id FROM COURSE_PREREQUISITES
WHERE prerequisite_course_id IN (2)
but it obviously fails since it returns all courses that satisfy at least one prerequisite which is not what I need. 但是它显然失败了,因为它返回了至少满足我所不需要的一个前提条件的所有课程。
I came across this question which is similar: Select rows that match all items in a list . 我遇到了类似的问题: 选择与list中所有项目匹配的行 。 But the provided solution does not work in my case, since the number of prerequisites for a course is not fixed.
但是提供的解决方案在我的情况下不起作用,因为课程的先决条件数量是不确定的。
Finally, I would also like to know if NOSQL databases (couchDB, mongoDB) are better suited for problems like these. 最后,我还想知道NOSQL数据库(couchDB,mongoDB)是否更适合此类问题。
accept cid;
select a.course_id from
(select course_id, max(prerequisite_course_id) as prerequisite_course_id from course_prerequisites
group by course_id
having count(*)=1) a
where a.prerequisite_course_id=&cid
union
select b.course_id from
(select course_id from course where course_id!=&cid) b
left join course_prerequisites c
on b.course_id=c.course_id where c.course_id is null;
The first half before the union is to get the course_id for the course which has the supplied input as prerequisite and the other half after the union is to select the courses that don't have any prerequisites. 联合之前的前半部分将获取具有输入的输入作为先决条件的课程的course_id,而联合之后的另一半将选择没有任何先决条件的课程。
This works in oracle. 这在oracle中有效。 The accept is to get input at run time.
接受是在运行时获取输入。 For the other dbs you can ignore the accept statement and pass in the course_id in place of &cid.
对于其他数据库,您可以忽略accept语句,并使用course_id代替&cid。
With a left join from COURSE
to COURSE_PREREQUISITES
: 从
COURSE
左转到COURSE_PREREQUISITES
:
select c.*
from course c left join course_prerequisites cp
on cp.course_id = c.course_id
where
c.course_id <> 2
and
(
cp.prerequisite_course_id is null
or
(
cp.prerequisite_course_id = 2
and
(select count(*) from course_prerequisites where course_id = c.course_id) = 1
)
)
order by c.course_id
Assuming you have for example two input courses (1,2)
then you may use the following query 假设您有两个输入课程
(1,2)
则可以使用以下查询
select distinct c.course_id
from courses c
left join course_prerequisites cp on cp.course_id = c.course_id
group by c.course_id
having count(case when cp.prerequisite_course_id not in (1,2) then 1 end) = 0
Assuming you only care about courses with pre-requisites, this should get what you want: 假设您只关心具有先决条件的课程,那么这应该会得到您想要的:
select cp.course_id
from course_prerequisites cp
group by cp.course_id
having count(*) = sum( prerequisite_course_id in ( <list of taken courses goes here> ) );
The sum()
is counting the number of courses that match the pre-requisites for a given course. sum()
正在计算与给定课程的前提条件匹配的课程数量。 The count()
. count()
。 The = count(*)
is requiring that this match the courses the student has taken. = count(*)
要求与学生所修课程匹配。
Then, there are the courses without pre-requisites. 然后,有些课程没有先决条件。 So:
所以:
(select cp.course_id
from course_prerequisites cp
group by cp.course_id
having count(*) = sum( prerequisite_course_id in ( <list of taken courses goes here> ) )
) union all
(select c.course_id
from courses c
where not exists (select 1
from course_prerequisites cp
where cp.course_id = c.course_id
)
);
You can actually do this without the union all
. 实际上,您可以在没有
union all
情况下执行此操作。 . 。 .:
。:
select c.course_id
from courses c left join
course_prerequisites cp
on c.course_id = cp.course_id
group by c.course_id
having count(cp.course_id) = sum( cp.prerequisite_course_id in ( <list of taken courses goes here> ) );
The logic here is the same as in the first query, except that courses with no pre-requisites are included and count(cp.course_id)
can be 0
. 此处的逻辑与第一个查询中的逻辑相同,除了其中包括没有先决条件的课程,并且
count(cp.course_id)
可以为0
。
In MySQL you can use FIND_IN_SET
to get the results you want using this query, which compares the number of courses completed with the number of pre-requisites of each course. 在MySQL中,您可以使用
FIND_IN_SET
通过此查询获取所需的结果,该查询将完成的课程数量与每个课程的先决条件数量进行比较。 The results includes courses which have no pre-requisites (where the student has not already completed that course). 结果包括没有先决条件的课程(如果学生尚未完成该课程)。
SET @courses_completed = '2';
SELECT c.course_id
FROM course c
LEFT JOIN course_prerequisites p ON p.course_id = c.course_id
WHERE NOT FIND_IN_SET(c.course_id, @courses_completed)
GROUP BY c.course_id
HAVING SUM(COALESCE(FIND_IN_SET(p.prerequisite_course_id, @courses_completed), 0) > 0) = COUNT(p.prerequisite_course_id);
Output: 输出:
course_id
1
3
I've made a demo on SQLFiddle with various values of @courses_completed
to show possible variants of courses the student is eligible for. 我做了一个关于SQLFiddle演示用的各种值
@courses_completed
展示课程的学生可享有可能的变体。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.