今天做sql题,看到这个题目后,就感觉完全相同这个限定很是特别,完全相同,意思显然是:1、选课数目相同,2、选择的课每个都相同。然后书上答案看着不对就去搜了搜,果真各有各的说法,看了几个博客,自己总结了一番,最终还是选择code更直观。
首先表结构只有一个:sc(sno,cno,grade)。表内数据如图,只有1001和1002是完全一样的。
note:下面给的sql语句为了能有1002方便对比,我没有加上其他同学条件,这个很简单,and 一个条件就够了。
错误答案1:
select sno
from sc where cno in(
select cno from sc where sno='1002')
group by sno
having count(*)=(
select count(*) from sc where sno='1002');
//错误结果:
+------+
| sno |
+------+
| 1001 |
| 1002 |
| 1003 |
+------+
3 rows in set
两个子查询很简单,一个是找到学号为1002的同学所选的所有课程,一个是1002同学所选的课程数。
首先理解的前提是,我们要知道sql的执行顺序:where子句是在group by ... having ...子句前执行的!group by子句是对where选择之后的元组有效,而不是对原来的整个表有效!!!这一点很多同学是完全没有注意过的。
然后这个语句的意思就是,1、先子查询中找到了1002学生选的所有课,2、再从所有选课记录中找到选择了这些课的行,3、再根据学号分组,4、看每个学号的选课的个数是否和1002一样。
这里的错误出在了2上面,找出了选择了这些课的行,但是选择其他课的行就被忽略了,所以1003能被选出来。1003选择了1,2,3课,多选的3被忽略掉了。
正确答案:
select sc1.sno
from (
select sno
from sc
group by sno
having count(*)=(
select count(*) from sc where sno='1002')) sc1, sc
where sc1.sno=sc.sno and cno in(
select cno from sc where sno='1002')
group by sc.sno
having count(*)=(
select count(*) from sc where sno='1002');
//答案结果
+------+
| sno |
+------+
| 1001 |
| 1002 |
+------+
2 rows in set
这里是:1、先在子查询找到1002所选的课数,2、找出选课数相等的学生学号并命名法新表sc1,3、sc1和sc连接后,可以排除掉所有选课数目不等于1002的学生的选课记录,4、重复上个答案的1、2,5、重复上个答案的3、4操作,得出答案。
note:网上有的答案是只有1、2、3、4,没有了5的,这样会造成的结果是1004也会被选出来,因为少了一步条件判断。
正确答案2:
select sno
from sc
where sno not in(
select sno from sc where cno not in(
select cno from sc where sno='1002'))
group by sno
having count(*)=(
select count(*) from sc where sno='1002');
这是一个让人感觉优秀的代码,not in双重否定看起来就让人觉得眼前一亮!出处在这里
思路: 1、子查询找出1002同学选的所有课,2、not in找出选了这些课以外的其他课的同学,3、再not in选择非2中选出的同学,及只选了1002所选的那些课的同学,4、对这些同学group by后判断选课数量是否一样