我正试图通过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
都没有通过的学生。我该怎么办?
答案 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);
形成学生和相关学科的笛卡尔积。
按学生(s.id
进行汇总,并过滤HAVING
子句中所有所有科目不及格的学生,并通过相关的{{1 }}对每个学生-学科组合,至少要对一个这样的失败课程进行子查询测试。
加入bool_and()
作为最后的修饰步骤,以获取学生姓名。我添加了EXISTS
以获得唯一的结果(因为名称可能不能保证唯一)。
根据实际的表定义,您的Postgres版本,基数和值分布,可能会有(很多)更有效的查询。
这是relational-division的核心。参见:
最有效的策略是在查询中尽可能早地消除尽可能多的学生,例如首先检查与失败学生最少的主题。然后仅继续处理其余的学生。
您的案例增加了特定的困难,即要测试的对象的数量和身份未知/动态。通常,递归CTE或类似的产品可为此类问题提供最佳性能: