查询以查找未通过所有给定科目的学生
Query to find students that failed all given subjects
我试图通过 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
的学生。我该怎么做?
我会使用聚合:
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);
Students fail a subject if they have a not null mark < 50 for at least one course offering of the subject.
许多种可能方式之一:
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
以获得唯一的结果(因为 names 可能不能保证是唯一的)。
根据实际 table 定义、您的 Postgres 版本、基数和值分布,可能会有(很多)更高效的查询。
这是一个 relational-division 的核心案例。参见:
- How to filter SQL results in a has-many-through relation
最有效的策略是尽可能早地在查询中排除尽可能多的学生——比如首先检查失败学生最少的科目。然后只处理剩下的学生等等
您的案例增加了待测对象的数量和身份未知/动态的具体困难。通常,递归 CTE 或类似的方法可以为此类问题提供最佳性能:
我试图通过 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
的学生。我该怎么做?
我会使用聚合:
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);
Students fail a subject if they have a not null mark < 50 for at least one course offering of the subject.
许多种可能方式之一:
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
以获得唯一的结果(因为 names 可能不能保证是唯一的)。
根据实际 table 定义、您的 Postgres 版本、基数和值分布,可能会有(很多)更高效的查询。
这是一个 relational-division 的核心案例。参见:
- How to filter SQL results in a has-many-through relation
最有效的策略是尽可能早地在查询中排除尽可能多的学生——比如首先检查失败学生最少的科目。然后只处理剩下的学生等等
您的案例增加了待测对象的数量和身份未知/动态的具体困难。通常,递归 CTE 或类似的方法可以为此类问题提供最佳性能: