sql - 查询以查找未通过所有给定科目的学生
问题描述
我试图通过 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
。我怎样才能做到这一点?
解决方案
如果学生在该科目的至少一门课程中的非空分 < 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);
形成学生和相关学科的Carthesian 产品。
按
s.id
学生(_ _HAVING
_bool_and()
_EXISTS
加入
people
作为最后的整容步骤以获得学生姓名。我添加id
以获得独特的结果(因为名称可能不保证是唯一的)。
根据实际的表定义、您的 Postgres 版本、基数和值分布,可能会有(很多)更有效的查询。
这是一个以关系划分为核心的案例。看:
最有效的策略是尽可能早地在查询中排除尽可能多的学生——比如首先检查不及格学生最少的科目。然后只处理剩下的学生等。
您的案例增加了要测试的对象的数量和身份是未知/动态的特定困难。通常,递归 CTE 或类似方法为此类问题提供最佳性能: