数据库作业8:SQL练习5 - SELECT(嵌套查询EXISTS、集合查询、基于派生表的查询)

EXISTS:存在量词,带有EXISTS谓词的子查询不返回任何数据:若where查询结果非空,产生逻辑真值‘true’;若where查询结果为空,产生逻辑假值‘false’。
由EXISTS引出的子查询,其目标列表达式通常都用 * ,因为带EXISTS的子查询只返回真值或假值,给出列名无实际意义。
[3.60]查询所有选修了1号课程的学生姓名。
方法一:等值连接

select sname
from student,sc
where student.sno = sc.sno 
and sc.cno='1';

方法二:exists查询

select sname
from student
where exists(
  select * 
  from sc
  where sno=student.sno 
  and cno='1');

两种方法的最终结果相同:
在这里插入图片描述

[3.61] 查询没有选修1号课程的学生姓名。

select sname 
from student
where not exists
  (select * 
  from sc
  where sno=student.sno and cno='1');

由于where后的exists只返回真值或者假值,因此,student表中的即使未选课(sc表中不存在)的学生姓名也会被查询到。
查询结果:
在这里插入图片描述
这个我也使用了第二种方法,但最终结果是错误的:

select sname
from student,sc
where student.sno = sc.sno and sc.cno<>'1';

原因:一个同学可以选择多门课程,比如A同学选了1,2,3号课程,如果使用上面语句,在1号课比对的时候,是不会有问题的;但2号课比对的时候因为符合条件,该同学的姓名将会被显示出来

[3.62]查询与“刘晨”在同一个系学习的学生。
方法一:exists嵌套查询

select sno,sname,sdept
from student s1
where exists 
  (select *
  from student s2
  where s2.sdept=s1.sdept
  and s2.sname='刘晨');

方法二:in嵌套查询:

select sno,sname,sdept
from student s1
where sdept in
  (select sdept
  from student
  where sname='刘晨');

方法三:等值连接查询

select fir.sno,fir.sname,fir.sdept
from student fir,student sec
where sec.sname='刘晨'
and fir.sdept=sec.sdept

查询的最终结果是相同的:
在这里插入图片描述
[3.63]查询选修了全部课程的学生姓名。
SQL语言中没有全称量词,但我们可以通过存在量词间接表达全称量词: ( ∀ x ) P ≡ (\forall x) P \equiv (x)P ¬ ( ∃ x ( ¬ P ) ) \neg(\exists x(\neg P)) ¬(x(¬P))

select sno,sname,sdept
from student 
where not exists //若下层返回F,则该层返回T
  (select *
  from course
  where not exists //对于该学号来说是否选了所有课程,若是,则返回F
    (select *
    from sc
    where sno=student.sno
    and cno = course.cno)
   );

not exists中,若内层查询结果非空,则外层的WHERE子句返回假值;若内层查询结果为空,则外层的WHERE子句返回真值。

集合查询

集合操作的种类

并-UNION
交-INTERSECT
差-EXCEPT

并-UNION
UNION:将多个查询结果合并起来时,系统自动去掉重复元组。
UNION ALL:将多个查询结果合并起来时,保留重复元组。
[3.64]查询计算机科学系的学生及年龄不大于19岁的学生。

select *
from student
where sdept='cs'
union select *
from student
where sage<=19;

查询结果:
在这里插入图片描述
[3.65]查询选修了课程1或者选修了课程2的学生。

select sno
from sc
where cno='1'
union select sno
from sc
where cno='2';

再次可以看出来,union其实相当于or,将所有符合条件的元组连起来。
[3.66]查询计算机科学系的学生与年龄不大于19岁的学生的交集。
方法一:等值连接

select sno,sname,sage,sdept
from student
where sdept='cs'and sage<=19;

方法二:

select *
from student
where sdept='cs'
intersect
select *
from student
where sage<=19;

查询结果:
在这里插入图片描述
由此可以看出来:intersect与and的作用是相同的。
[3.67]查询既选修了课程1又选修了课程2的学生。
方法一:

select sno 
from sc
where cno='1'
intersect
select sno 
from sc
where cno='2';

方法二:嵌套查询

select sno
from sc
where cno='1' and sno in
     (select sno
     from sc
     where cno='2');

[3.68]查询计算机科学系的学生与年龄不大于19岁的学生的差集。
方法一:

select * 
from student
where sdept='cs'
except select *
from student 
where sage<=19;

方法二:

select *
from student
where sdept='cs' and sage>19;

基于派生表的查询

子查询不仅可以出现在WHERE子句中,
还可以出现在FROM子句中,
这时子查询生成的临时派生表成为主查询的查询对象。

[3.57]找出每个学生超过他自己选修课程平均成绩的课程号。
方法一:

select sno,cno
from sc,(select sno,avg(grade)
   from sc 
    group by sno)
    as avg_sc(avg_sno,avg_grade)
where sc.sno=avg_sc.avg_sno
and sc.grade>=avg_sc.avg_grade

方法二:

select sno,cno
from sc x
where grade>=(select avg(grade)
    from sc y
    where y.sno=x.sno);

查询结果:
在这里插入图片描述

[3.60]查询所有选修了1号课程的学生姓名。
方法一:

select sname
from student
where exists(
select *
from sc
where sno=student.sno
and cno='1');

方法二:

select sname
from student,sc
where sc.sno=student.sno 
and cno='1';

方法三:

select sname
from student,(select sno 
from sc 
where cno='1')
as sc1
where student.sno = sc1.sno;

查询结果:
在这里插入图片描述

select总结

select语句一般格式:

 SELECT [ALL|DISTINCT]  <目标列表达式> [别名] [ ,<目标列表达式> [别名]]FROM  <表名或视图名> [别名] [ ,<表名或视图名> [别名]]|(<SELECT语句>)[AS]<别名>
 [WHERE <条件表达式>]
 [GROUP BY <列名1> [HAVING<条件表达式>]]
 [ORDER BY <列名2> [ASC|DESC]];

其中:
1、from子句组装来自不同数据源的数据;
2、where子句基于指定的条件对记录行进行筛选,从表中选择满足条件的组;
3、group by子句将数据划分为多个分组,其执行顺序从左往右分组;
4、使用聚集函数进行计算;
5、使用having子句筛选分组,从组中选择满足条件的组;
6、计算所有的表达式;
7、使用order by对结果集进行排序。

select查询主要分为:单表查询、连接查询、嵌套查询、集合查询以及基于派生表的查询。连接查询是多个表的查询,嵌套查询是通过带IN谓词、带比较运算符、带有ANY或ALL谓词的子查询块嵌套在WHERE子句或HAVING短语的条件中。集合查询主要包括并操作UNION、交操作INTERSECT和差操作EXCEPT,但是参加集合操作的各查询结果的列数必须是相同的,对应项的数据类型也必须是相同的。在基于派生表的查询中,where中的子查询可出现在from中,子查询生成的临时派生表成为主查询的查询对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值