查询以查找未通过所有给定学科的学生

时间:2019-10-16 23:44:48

标签: sql postgresql relational-division

我正试图通过PostgreSQL查询来找到一组科目中每个科目都没有通过的学生。

如果学生对至少一门课程的科目得分不低于50,则该科目将不及格。我想找到一组Relevant_subjects科目中所有科目都没有通过的学生。
注意:每个课程的学生可以有几条记录。

SELECT People.name
FROM 
    Relevant_subjects
    JOIN Courses on (Courses.subject = Relevant_subjects.id)
    JOIN Course_enrolments on (Course_enrolments.course = Courses.id)
    JOIN Students on (Students.id = Course_enrolments.student)
    JOIN People on (People.id = Students.id)
WHERE
    Course_enrolments.mark is not null AND
    Course_enrolments.mark < 50 AND
;

使用上面的代码,我得到了所有Relevant_subjects都没有通过的学生,但是我想要的结果是得到了所有Relevant_subjects都没有通过的学生。我该怎么办?

2 个答案:

答案 0 :(得分:1)

我将使用聚合:

SELECT p.name
FROM Relevant_subjects rs JOIN
     Courses c
     ON c.subject = rs.id JOIN
     Course_enrolments ce
     ON ce.course = c.id JOIN
     Students s
     ON s.id = ce.student JOIN
     People p
     ON p.id = s.id
WHERE ce.mark < 50
GROUP BY p.id, p.name
HAVING COUNT(*) = (SELECT COUNT(*) FROM relevant_subjects);

注意:此版本假定学生每门课程只有一条记录,并且relevant_subjects没有重复。如有必要,可以使用COUNT(DISTINCT)轻松地处理这些问题。

要处理重复项,应如下所示:

SELECT p.name
FROM Relevant_subjects rs JOIN
     Courses c
     ON c.subject = rs.id JOIN
     Course_enrolments ce
     ON ce.course = c.id JOIN
     Students s
     ON s.id = ce.student JOIN
     People p
     ON p.id = s.id
WHERE ce.mark < 50
GROUP BY p.id, p.name
HAVING COUNT(DISTINCT rs.id) = (SELECT COUNT(DISTINCT rs2.id) FROM relevant_subjects rs2);

答案 1 :(得分:1)

  

如果学生对至少一门课程的科目得分不低于50,则该科目将不及格。

许多一种可能的方式:

SELECT id, p.name
FROM  (
   SELECT s.id
   FROM   students                s
   CROSS  JOIN relevant_subjects rs
   GROUP  BY s.id
   HAVING bool_and( EXISTS(
            SELECT -- empty list
            FROM   course_enrolments ce
            JOIN   courses           c  ON c.id = ce.course
            WHERE  ce.mark < 50  -- also implies NOT NULL
            AND    ce.student = s.id
            AND    c.subject = rs.id
            )
         ) -- all failed
   ) sub
JOIN   people p  USING (id);
  1. 形成学生和相关学科的笛卡尔积。

  2. 按学生(s.id进行汇总,并过滤HAVING子句中所有所有科目不及格的学生,并通过相关的{{1 }}对每个学生-学科组合,至少要对一个这样的失败课程进行子查询测试。

  3. 加入bool_and()作为最后的修饰步骤,以获取学生姓名。我添加了EXISTS以获得唯一的结果(因为名称可能不能保证唯一)。

根据实际的表定义,您的Postgres版本,基数和值分布,可能会有(很多)更有效的查询。

这是的核心。参见:

最有效的策略是在查询中尽可能早地消除尽可能多的学生,例如首先检查与失败学生最少的主题。然后仅继续处理其余的学生。

您的案例增加了特定的困难,即要测试的对象的数量和身份未知/动态。通常,递归CTE或类似的产品可为此类问题提供最佳性能: