我有下表:
课程
+----------+-------------------------+
| course_id | course_name |
+-----------+------------------------+
| 1 | s001 |
| 2 | s002 |
| 3 | s003 |
| 4 | s004 |
+-----------+------------------------+
COURSE_PREREQUISITES
+----------+-------------------------+
| course_id | prerequisite_course_id |
+-----------+------------------------+
| 3 | 2 |
+-----------+------------------------+
| 4 | 1 |
+-----------+------------------------+
| 4 | 2 |
+-----------+------------------------+
| 4 | 3 |
+-----------+------------------------+
我的问题是:给定学生已完成的课程ID的列表,我如何获得该学生有资格参加的课程的列表?
示例
如果学生已完成course_id
2,则查询应返回课程:1,(因为它没有先决条件)和3,但不是4,因为4也有1,3作为先决条件。
尝试解决方案
对于已经完成课程2的学生,我尝试使用IN
语句,就像这样:
SELECT DISTINCT course_id FROM COURSE_PREREQUISITES
WHERE prerequisite_course_id IN (2)
但是它显然失败了,因为它返回的所有课程至少满足一个我不需要的前提条件。
我遇到了一个类似的问题: Select rows that match all items in a list。但是,对于我来说,所提供的解决方案不起作用,因为课程的先决条件数量不是固定的。
最后,我还想知道NOSQL数据库(couchDB,mongoDB)是否更适合此类问题。
答案 0 :(得分:2)
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;
联合之前的上半部分是获取以提供的输入为前提的课程的Course_id,联合之后的另一半是选择没有任何前提的课程。
这在oracle中有效。接受是在运行时获取输入。对于其他数据库,您可以忽略accept语句,并使用course_id代替&cid。
答案 1 :(得分:1)
从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
请参见demo
答案 2 :(得分:1)
假设您有两个输入课程(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
答案 3 :(得分:1)
假设您只关心具有 个先决条件的课程,这应该会得到您想要的:
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> ) );
sum()
正在计算与给定课程的前提条件匹配的课程数量。 count()
。 = count(*)
要求该课程与学生所修课程匹配。
然后,有些课程没有先修课程。所以:
(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
)
);
您实际上可以在没有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> ) );
此处的逻辑与第一个查询中的逻辑相同,除了其中包括没有先决条件的课程,并且count(cp.course_id)
可以是0
。
答案 4 :(得分:1)
在MySQL中,您可以使用FIND_IN_SET
通过此查询获取所需的结果,该查询将完成的课程数与每个课程的先决条件数进行比较。结果包括没有先决条件的课程(学生尚未完成该课程的课程)。
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);
输出:
course_id
1
3
我制作了一个demo on SQLFiddle,其中包含@courses_completed
的各种值,以显示该学生有资格参加的课程的变体。