针对这类匹配问题(匹配数量和内容),例如这道题,需要和学号01的课程完全相同,可以使用连接查询来进行匹配。
一般来说,在C、Java语言中,我们用循环来遍历两个list,然后判断size()、各个元素是否相同。在SQL中,这种遍历比较的思想,可以用笛卡尔积来实现,具体来说就是内连接(inner join)、外连接(left/right outer join)、交叉连接(cross join)。
(1)外连接
SELECT s.* FROM Student as sWHERE s.s_id IN(SELECT sc1.s_id FROM Score sc1LEFT JOIN (SELECT DISTINCT c_id FROM Score WHERE s_id=01) sc2 ON sc1.c_id=sc2.c_idWHERE sc1.s_id<>01GROUP BY sc1.s_idHAVING COUNT(sc1.c_id)=(SELECT DISTINCT COUNT(c_id) FROM Score WHERE s_id=01) AND COUNT(sc2.c_id)=COUNT(sc1.c_id) -- 自身课程数=01课程数,且,自身课程=01课程);
a.读题可以直观地得到以下两个值:
—01号学生的课程:SELECT DISTINCT c_id FROM Score WHERE s_id=01
— 01号学生的课程总数:SELECT DISTINCT COUNT(c_id) FROM Score WHERE s_id=01
b.选出和01号学生课程完全相同的学生,主要是得到符合条件的sid,就能查出学生信息。所以用所有学生的选课和01号学生的课程逐个作比较。因为左连接可以更好地保留那些不符合条件的学生,以备进一步筛选。
SELECT sc1.s_id FROM Score sc1
LEFT JOIN (SELECT DISTINCT c_id FROM Score WHERE s_id=01) sc2 ON sc1.c_id=sc2.c_id
c.然后逐步添加筛选条件,在having子句中:
COUNT(sc2.c_id)=COUNT(sc1.c_id)
其实应该写作COUNT(sc2.c_id)=(SELECT DISTINCT COUNT(c_id) FROM Score WHERE s_id=01),保证 筛选出的学生的课程,包含了01学生所选的所有课程(即:01号学生的课程 是 某个学生的课程 的子集)。
COUNT(sc1.c_id)=(SELECT DISTINCT COUNT(c_id) FROM Score WHERE s_id=01)
这个筛选条件保证 学生的课程数=01号学生的课程数,防止 01号学生的课程 是 某个学生的课程 的真子集。
是子集但不是真子集,所以只能是相等。
(2)内连接
这是一种正向查询的思想,先筛选出其他学生中选修的课程数量和01号学生选修的课程数量相同的学生的学生编号和所修课程编号,然后将筛选结果与01号学生选修的课程编号进行内连接,然后再根据01号学生选修课程的数量做最后的筛选。
select * from student where sid in(select sid from(select sid,cid from grade2 where sid in(select sid from grade2 where sid !='01' group by sidhaving count(*)=(select count(*) from grade2 where sid='01' group by sid))) as t1inner join (select cid from grade2 where sid='01') as t2on t1.cid = t2.cidgroup by t1.sidhaving count(*)=(select count(*) from grade2 where sid='01'))
