一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

如何利用SQL子查询找出选修了所有课程的优等生名单?

时间:2026-07-02 11:16:45 编辑:袖梨 来源:一聚教程网

直接用IN或EXISTS无法表达“选过所有课程”的逻辑,因其仅判断集合包含关系;正确解法是分组统计每个学生选课数并与有效课程总数比对,需用COUNT(DISTINCT course_id)并同步过滤课程状态。

为什么直接用 INEXISTS 子查询不行?

很多人一上来就写 SELECT student_id FROM enrollments WHERE course_id IN (SELECT course_id FROM courses),结果得到的是“选过任意课程”的学生,而不是“选过所有课程”的人。核心在于:子查询必须表达“对每门课都存在对应选课记录”,不是简单集合包含关系。

COUNT(DISTINCT ...) + 分组比对是最稳的解法

前提是:学生-课程关系存于 enrollments 表(字段为 student_id, course_id),课程总数可查得。关键思路是——算出每个学生选了多少门课,再和总课程数比对是否相等。

  • 先确认总课程数:SELECT COUNT(*) FROM courses
  • 再分组统计每个学生的选课数:SELECT student_id, COUNT(DISTINCT course_id) AS cnt FROM enrollments GROUP BY student_id
  • 最后用 HAVING 筛出等于总课程数的:HAVING cnt = (SELECT COUNT(*) FROM courses)

注意必须用 COUNT(DISTINCT course_id),避免同一学生重复选同一门课导致计数虚高。

如果还要加“优等生”条件(比如 GPA ≥ 90),别在子查询里硬塞

把成绩筛选和“选全课程”拆开做,否则容易漏数据或逻辑错乱。典型错误是写成 WHERE gpa >= 90 AND student_id IN (SELECT ...),但子查询里没关联成绩表,student_id 可能根本不在成绩表中。

  • 正确做法:先用上一步拿到“选全课程”的 student_id 列表,再和 studentsgradesJOIN
  • 例如:SELECT s.name FROM students s JOIN (SELECT student_id FROM enrollments GROUP BY student_id HAVING COUNT(DISTINCT course_id) = (SELECT COUNT(*) FROM courses)) t ON s.id = t.student_id WHERE s.gpa >= 90
  • 如果 gpa 存在另一张表(如 grades),记得 JOIN 时处理可能的 NULL ——用 INNER JOIN 自然过滤掉无成绩者,比 LEFT JOIN + WHERE gpa IS NOT NULL 更安全

遇到课程表有状态字段(如 is_active = 1)时,子查询里的 COUNT 必须同步过滤

否则会拿“全部课程数”去比,但学生实际只需选完当前有效的课。常见坑是子查询里忘了加 WHERE is_active = 1,导致本该入围的学生被筛掉。

  • 总课程数要改成:(SELECT COUNT(*) FROM courses WHERE is_active = 1)
  • 学生选课统计也要限制课程有效性:COUNT(DISTINCT CASE WHEN c.is_active = 1 THEN e.course_id END),或者更干脆——先 JOIN coursesWHERE c.is_active = 1
  • 别依赖外层 WHERE 过滤课程状态,因为分组统计必须基于同一有效课程集

多一层状态判断,就多一个对齐点;漏掉任何一个,结果就偏了。

热门栏目