【问题标题】:SQL Query to select subjects that satisfy prerequisite requirementsSQL 查询以选择满足先决条件要求的主题
【发布时间】:2019-02-10 10:15:40
【问题描述】:

我有以下表格:

课程

+----------+-------------------------+
| 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)是否更适合此类问题。

【问题讨论】:

    标签: mysql sql


    【解决方案1】:
    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,而后半部分是选择没有任何先决条件的课程。

    这适用于甲骨文。接受是在运行时获取输入。对于其他数据库,您可以忽略接受语句并传入 course_id 代替 &cid。

    【讨论】:

    • mysql 中没有减号。
    • 感谢@P.Salmon 的留言。将其更改为左连接。
    【解决方案2】:

    在 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 的值各不相同,以显示学生有资格参加的课程的可能变体。

    【讨论】:

    【解决方案3】:

    COURSECOURSE_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

    【讨论】:

    • 感谢您的回答。这确实有效,但如果输入包含学生已完成的多个科目怎么办。说:1,2,3
    • 检查这个db-fiddle.com/f/bwbPKYbAh5K5Kf9zd54yJ1/2,告诉我它是否适用于2和3的情况。没有足够的数据来测试。
    • 适用于 (2,3) 但似乎不适用于应该返回 4 作为唯一合格课程的情况 (1,2,3)。
    • 这个db-fiddle.com/f/bwbPKYbAh5K5Kf9zd54yJ1/3 给你你想要的结果:将条件更改为=3,使其匹配项目数(1,2,3)
    • 或者条件应该是&lt;=3。如果您有更多数据,请测试它。
    【解决方案4】:

    假设您有两个输入课程(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
    

    【讨论】:

    • 感谢您的提问。但是数据库仅包含问题中描述的课程信息。 API 接收学生已完成的课程列表,我们必须使用该列表来使用数据库获取符合条件的课程列表。
    • 好的,现在我得到了要求。我已经更新了答案。
    【解决方案5】:

    假设你只关心课程先决条件,这应该得到你想要的:

    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

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-18
    • 2014-02-18
    • 2017-10-22
    • 2019-04-24
    • 2014-10-20
    • 2021-03-14
    相关资源
    最近更新 更多