PostgreSQL联表查询如何筛选同时选修A、B、C、D全部课程的学生
查询同时选修指定全部课程的学生实现方案
核心匹配逻辑:筛选出的学生,在选课关联表中必须同时存在与A、B、C、D四门课对应的选课记录,不存在缺漏。
下面是几种可落地的实现方式:
方式1:分组计数匹配(最易理解的通用写法)
核心逻辑:先把所有匹配四门目标课程的选课记录过滤出来,按学生维度分组,统计每个学生覆盖到的目标课程数量,只要数量等于4,就说明该学生选全了四门课。
写法说明:统计时加DISTINCT避免选课表出现重复脏数据导致计数虚高,无需学生实际选课总数刚好为4——学生额外选其他课程不影响结果。
SELECT s.student_id, s.student_name FROM student s -- 关联选课表 INNER JOIN sc ON s.student_id = sc.student_id -- 关联课程表匹配课程名 INNER JOIN course c ON sc.course_id = c.course_id WHERE c.course_name IN ('A', 'B', 'C', 'D') GROUP BY s.student_id, s.student_name -- 统计去重后的匹配课程数,和目标课程总数对齐 HAVING COUNT(DISTINCT sc.course_id) = 4;
方式2:逐门课做存在性校验(大表场景性能更优)
核心逻辑:对每个学生,逐次校验他是否存在选修A的记录、是否存在选修B的记录……四个校验全部通过,就符合要求。
这种写法不需要对全量选课记录做分组聚合,尤其适合选课表数据量很大、目标课程数量少的场景,数据库可以借助学生id、课程id的索引快速定位校验,执行效率更高。
SELECT s.student_id, s.student_name FROM student s WHERE -- 逐门校验是否有对应选课记录 EXISTS ( SELECT 1 FROM sc INNER JOIN course c ON sc.course_id = c.course_id WHERE sc.student_id = s.student_id AND c.course_name = 'A' ) AND EXISTS ( SELECT 1 FROM sc INNER JOIN course c ON sc.course_id = c.course_id WHERE sc.student_id = s.student_id AND c.course_name = 'B' ) AND EXISTS ( SELECT 1 FROM sc INNER JOIN course c ON sc.course_id = c.course_id WHERE sc.student_id = s.student_id AND c.course_name = 'C' ) AND EXISTS ( SELECT 1 FROM sc INNER JOIN course c ON sc.course_id = c.course_id WHERE sc.student_id = s.student_id AND c.course_name = 'D' );
两种写法没有绝对的优劣,可以根据实际数据规模选择:选课表数据量较小时选分组计数的写法代码更简洁、维护成本低;数据量较大时优先选存在性校验的写法,能借助索引减少全表扫描开销。
常见踩坑提醒
- 不要直接统计学生的总选课数等于4:很可能把选了四门无关课程、根本没碰A/B/C/D的学生错误匹配进来
- 不要只加
WHERE c.course_name IN ('A','B','C','D')就直接返回结果:这种写法返回的是任意选了其中一门课的学生,不是四门全选的 - 不要写不相关的子查询做集合匹配:比如直接写
WHERE sc.student_id IN (选修A的学生列表)这种,无法保证同一个学生同时满足多门课的匹配条件,会出现结果偏差
注:如果后续需要调整匹配的课程列表,只要对应修改
IN后面的课程名、调整HAVING后面的计数阈值即可,无需改动整体逻辑结构。
内容的提问来源于stack exchange,提问作者g_auge19
相关产品推荐
相关产品推荐

