如何统计同时选修两门特定CRN课程的学生SID数量?
如何统计同时选修CRN=12345和CRN=20976两门课程的学生SID数量?
你好!看起来你已经离解决问题很近了——能筛选出符合条件的SID,只差一步统计总数而已。我来帮你梳理下正确的实现方法,顺便解释下之前的写法为什么无效。
首先先明确你的场景:
你有一张takes表,数据如下:
sid | crn | grade 12321| 12345 | A 12321| 20976 | B 21008| 12345 | C 21008| 20976 | A 21008| 28469 | D 21090| 12345 | C 21090| 20976 | F
你已经写出了能正确筛选出同时选修两门课的SID的SQL:
select sid from takes where crn in (12345 , 20976 ) group by sid HAVING COUNT(*) = 2
但你尝试的几种统计写法都没达到预期,比如:
-- 写法1:返回每个符合条件SID的计数(都是1),不是总数 select count(sid) from takes where crn in (12345 , 20976 ) group by sid HAVING COUNT(*) = 2 -- 写法2:返回每个SID和对应的选课数,不是总数 select sid, count(sid) from takes where crn in ( '12345', '20976') GROUP By sid HAVING count(*) > 1 -- 写法3:语法错误,子查询返回的SID列表没有和外层表关联 select count(sid) from takes where (select sid from takes where crn in ( '12345', '20976') GROUP By sid HAVING count(*) > 1 ) -- 写法4:同样语法错误,且逻辑上还是按SID分组返回单条记录 select sid, count(sid) from takes where (select sid from takes where crn in ( '12345', '20976') HAVING count(*) > 1 ) GROUP BY sid
正确的统计方法
这里有两种简单有效的写法,你可以根据自己的场景选择:
方法1:子查询包裹统计(最直观)
既然你已经能拿到符合条件的SID列表,只需要把这个查询作为子查询,在外层用COUNT(*)统计行数即可:
SELECT COUNT(*) AS student_count FROM ( SELECT sid FROM takes WHERE crn IN (12345, 20976) GROUP BY sid HAVING COUNT(*) = 2 ) AS eligible_students
这个写法逻辑清晰:先筛选出所有同时选了两门课的学生SID,再统计这个列表的总数量。
方法2:用EXISTS关联查询(性能更优)
如果你的takes表中不会出现同一个学生重复选同一门CRN课程的情况(即(sid, crn)是唯一组合),可以用这种写法,它能更好地利用索引,在数据量大的时候性能更好:
SELECT COUNT(DISTINCT t1.sid) AS student_count FROM takes t1 WHERE t1.crn = 12345 AND EXISTS ( SELECT 1 FROM takes t2 WHERE t2.sid = t1.sid AND t2.crn = 20976 )
思路是:先找到所有选了CRN=12345的学生,然后检查这些学生是否同时选了CRN=20976,最后统计去重后的学生数量。
为什么之前的写法无效?
简单解释下你尝试的写法问题:
- 写法1和写法2:都是按
sid分组,所以返回的是每个符合条件的学生的单独统计结果,而不是所有学生的总数。 - 写法3和写法4:存在语法错误,子查询返回的SID列表没有和外层的
takes表建立关联条件,数据库无法正确执行查询。
内容的提问来源于stack exchange,提问作者O'Kara
相关产品推荐
相关产品推荐

