查询至少选修了学生201215122选修的全部课程的学生号码

        这个条SQL语句是来自王姗老师《数据库系统概论》书中111页的例子。SQL语句是在我自己建立的表的基础上写出来的。SQL语句如下

select distinct Sno
from tb_SC x
where not exists
(
	select *
	from tb_SC y
	where y.Sno='20173824001' and not exists
	(
		select *
		from tb_SC Z
		where z.Cno=y.Cno and 
		z.Sno=x.Sno
	)
)

        首先,题目要我们所求的是一名学生的学号,但是这个名学生X必须至少选修了学号为201215122学生Y的全部课程,当然学生X也可以选修多余学生Y的课程。因为SQL中没有全称量词,所以,这里得使用否定之否定来实现。

        第一:求出学生Y选修的所有课程;第二:把学生X的学号拿到SC表里面去对,看看是否满足所有的记录都在SC里面。

        举个例子:X={2017001,2017002},Y={数据库,操作系统,数据结构},Z={2017001=数据库,2017001=操作系统,2017002=数据结构}。其中X是所有学生学号的集合,Y是201215122同学选修的所有课程的集合,Z是一张选修课程表。那么首先我们取出X中的第一条记录2017001,去和Y中第一条记录数据库组合,形成新记录W:2017001=数据库,然后拿W去Z中找,看看能不能找到W。显然,这条记录可以找到。那么继续操作,从Y中取出第二条记录操作系统,组合成W:2017001=操作系统,拿W去Z里面发现还是可以找到。继续从Y里面拿出一条记录数据结构,组合成W:2017001=数据结构,去Z里面找,发现没有找到。那么这个时候没有找到,说明了一个什么问题。证明2017001没有选修数据结构这门课,但是Y选修了这门课(Y是201215122同学选修的所有课程的集合),所以X中的2017001不符合题意。

        看到这里应该就对它的写法的一个思想理解了。接下来具体到SQL语句去理解。以下内容为个人理解,可能存在错误的地方,望大家留言评论。

        首先看到上面的思想,就知道的使用循环来实现这个目的,在我的另外一篇博客里面就说到,相关子查询就是一个循环。所以,这里得使用相关子查询来实现。

        其次前面说了,要使用否定之否定来做,所以要使用not exists。而且,对于exists和not exists来说,也必须和相关子查询来搭配使用。(个人理解)

        我们看到第二重查询中有一个 y.Sno='20173824001’这个就是为了后面三张表在做连接的使用导致y.Sno只能是20173824001,那么就会导致y.Cno只能是20173824001所选择的课程的Cno(这个也就是我们的目的),所以,z.Cno也会被限制住。z.Cno被限制后,导致z.Sno也被限制住,同时影响x.Sno。如果大家不理解的画,可以先单独写一哈这样的语句:

select * 
from tb_SC x,tb_SC y,tb_SC z
where x.Sno=z.Sno and y.Cno=z.Cno

这个就是把三张表连接起来,至于为什么要这样连,不写成

where x.Cno=z.Cno and y.Sno=z.Sno

这个原因就是上面的那段话,通过一些限制就可以达到这个效果了。我还试过这样写:

select distinct Sno
from tb_SC x
where not exists
(
	select *
	from tb_SC y
	where  not exists
	(
		select *
		from tb_SC Z
		where z.Cno=y.Cno and 
		z.Sno=x.Sno and y.Sno='20173824001'
	)
)

结果也是错误的。为什么也是错误的呢?我是这样想的。对于正确写法而言,我们最终相当于是在下面这个SQL语句得出的表中匹配结果。

select *
from tb_SC x,tb_SC y,tb_SC z
where z.Cno=y.Cno and 
z.Sno=x.Sno

上面的结果得出的表,我截一部分来看看:结果有很多组合!
在这里插入图片描述
但是按照错误的写法来看,我们就是在这样的SQL语句得出的表中来匹配结果:

select *
from tb_SC x,tb_SC y,tb_SC z
where z.Cno=y.Cno and 
z.Sno=x.Sno and y.Sno='20173824001'

这个是它的结果:结果相对来说就很少了。
在这里插入图片描述
        最后,大家可以看到,写成错误的写法,显然会将有些应该匹配的数据丢失。记得,当你之后回头复习发现还是不会的时候,可以自己把上面的函数去执行一下就可以了,看看结果到底是什么。图片终究是不真实的,自己实际看到的才真实。

        最上面的结果是会返回学生20173824001的,但是我现在要查询的是:至少选修了学生20173824001选修的全部课程的其他学生的学号。

select distinct Sno
from tb_SC x
where not exists
(
	select *
	from tb_SC y
	where y.Sno='20173824001' and not exists
	(
		select *
		from tb_SC Z
		where z.Cno=y.Cno and 
		z.Sno=x.Sno  and x.Sno!='20173824001'
	)
)

        说了很多,其实还是得自己多多理解,这个复杂的SQL语句靠别人是学不会的,的自己理解。加油?

-------------------------------------------------------分割线----------------------------------------------------------
以下内容是25 jun 补充:

        

查询至少选修了0001号和0002号课程的学生的学号。

一开始遇到这个题目,就想到的使用否定之否定。但是,发现自己写不出来了。欸,只能说自己还是没有掌握吧!为什么说这里可以不用使用否定之否定呢?是因为上面是要我们求“至少选修了学生201215122选修的全部课程的学生号码”,我们并不知道201215122到底选修了什么。但是这题不一样,题目明确说了,就是至少选修了0001和0002号课程的学生。所以,我们使用一个谓词in就可以了。SQL语句如下:

select distinct Sno
from tb_SC
where Sno in(
select x.Sno
from tb_SC x,tb_SC y
where x.Sno=y.Sno and x.Cno='0001' and y.Cno='0002');

如果这里没有加上distinct的话,就会有很多重复的记录,就像这个样子
在这里插入图片描述
原因很简单,就是因为嵌套查询的原理都是拿出一条元组记录一个一个去比配,满足的话就放到结果集中去。所以,因为选课表是一个学生可能选择多门课,所以,就会有重复的记录了。

        还有为什么要使用自身连接,这个为之前的博客里面有说到过,链接给大家去看啦!https://blog.csdn.net/suliangkuanjiayou/article/details/93108945

-------------------------------------------------------------------分割线----------------------------------------------
以下内容2.july补充:

        还是之前的老问题,但是我现在发现了一个新的东西。
问题:查询选修了所有课程的学生学号。SQL语句如下

select Sno
from tb_Student
where not exists(
select *
from tb_Course
where not exists(
select * 
from tb_SC
where tb_SC.Cno=tb_Course.Cno and 
tb_SC.Sno=tb_Student.Sno
)
)

我发现这里是用到了tb_Student,tb_Course,tb_SC三张表。
但是对于一个同样的类似的问题,
问题:查询至少选修了1001号老师所有课程的学生的学号。SQL语句如下:

select distinct Sno
from tb_SC x
where not exists(
select *
from tb_Course,tb_Teacher
where tb_Course.Tno=tb_Teacher.Tno
and tb_Teacher.Tno='1001' and not exists(
select * 
from tb_SC y
where x.Sno=y.Sno and y.Cno=tb_Course.Cno
)
)

但是这里仅仅使用了tb_SC表,和tb_Course表(tb_Teacher表忽略,因为和它关系不大)。我就在想,我觉得按照之前我的想法,这里写成这这样子应该也是没有问题的:

select  Sno
from tb_Student
where not exists(
select *
from tb_Course,tb_Teacher
where tb_Course.Tno=tb_Teacher.Tno
and tb_Teacher.Tno='1001' and not exists(
select * 
from tb_SC 
where tb_Student.Sno=tb_SC.Sno and tb_SC.Cno=tb_Course.Cno
)
)

因为按照我之前博客的讲法,就是从tb_Student表中拿出一条记录,和tb_Course中1001号老师讲授的所有课程进行组合,如果在tb_SC中可以找到所有的记录,则说明这个学生选修了所有的课程。然后,事实告诉我们,还真的就是对的。哈哈哈哈。

        细心的同学可能知道了,第一个答案上面使用distinct的,不然出来的结果就是会有很多重复的,但是后面那个方法是没有加上distinct关键字的,但是结果还是正确无误的。可以用心体会以下这个的区别

  • 66
    点赞
  • 216
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值