1.在SQL语言中,一个select-from-where语句被称为是一个查询块;将一个查询块嵌套在另一个查询块的where子句或having短语的条件中的查询称为嵌套查询
2.为了举例和实验方便,先建立三个表:学生表(student),课程表(course),选课表(sc)
--学生表 create table student( sno int primary key auto_increment, sname varchar(10), ssex char(1), sage int(3), sdept varchar(5) ); --课程表 create table course( cno int primary key auto_increment, cname varchar(10) ); --选课表 create table sc( sno int, cno int, grade int(3), primary key(sno,cno), foreign key(sno) references student(sno), foreign key(cno) references course(cno) );
3.父查询和子查询
select sname from student where sno in ( select sno from sc where cno=2 );
例子中的select sname ...是外层查询或父查询,select sno ...是内层查询或子查询
4.SQL语言允许多层嵌套查询,即一个子查询中还能嵌套其他子查询;注意子查询中不能使用order by子句,order by子句只能对最终查询结果排序
5.不同类型的子查询
①带有in谓词的子查询
--例1:查询与刘晨在同一个系学习的学生 select sno,sname,sdept from student where sdept in (select sdept from student where sname='刘晨'); --步骤分析 --①确定刘晨所在的系名 --select sdept from student where sname='刘晨'; 结果为CS --②查找所有在CS系学习的学生 --select sno,sname,sdept from student where sdept='CS'; --本例中的查询也可以用自身连接来完成 select s1.sno,s1.sname,s1.sdept from student s1,student s2 where s1.sdept=s2.sdept and s2.sname='刘晨';
--例2:查询选修了课程名为“信息系统”的学生学号和姓名 select sno,sname from student where sno in(select sno from sc where cno in(select cno from course where cname='信息系统')); --本例中的查询也可以用自身连接完成 select student.sno,sname from student,sc,course where student.sno=sc.sno and sc.cno=course.cno and course.cname='信息系统'; --有些嵌套查询可以用连接运算替代,有些不行
子查询的查询条件不依赖于父查询,称为不相关子查询;子查询的查询条件依赖于父查询,称为相关子查询;区分相关子查询和不相关子查询的方法是看子查询能否独立运行,能独立运行的是不相关子查询,不能独立运行的是相关子查询
②带有比较运算符的子查询(子查询的结果如果是一个值,可以用=代替in)
--例3:找出每个学生超过他自己选修课程平均成绩的课程号 select sno,cno from sc x where grade>=(select avg(grade) from sc y where y.sno=x.sno); --x是表sc的别名,又称为元组变量,可以用来表示sc的一个元组 --内层查询是求一个学生所有选修课程的平均成绩的,至于是哪个学生的平均成绩要看参数x.sno的值,而该值是与父查询有关的,因此这类查询被称为相关子查询 --一种可能的执行过程 --①从外层查询中取出sc的一个元组x,将元组x的sno值(201215121)传给内层查询 --select avg(grade) from sc y where y.sno='201215121'; --②执行内层查询,得到88(近似值),用该值代替内层查询,得到外层查询 --select sno,cno from sc x where grade>=88; --③从外层查询取出下一个元组重复上述步骤,知道外层的sc元组全部处理完毕 --求解相关子查询不能像求解不相关子查询那样一次将子查询求解出来,然后求解父查询。 --内层查询与外层查询有关,因此必须反复求值
③带有any(some)或all谓词的子查询
子查询返回单值是可以用比较运算符,但返回多值时要用any(有些系统使用some)或all谓词修饰符。而使用any或all谓词时必须同时使用比较运算符
扫描二维码关注公众号,回复:
10940843 查看本文章
any(some)、all谓词与聚集函数、in谓词的等价转换关系表
--例4:查询非CS系中比CS系任意一个学生年龄小的学生姓名和年龄 select sname,sage from student where sage<any(select sage from student where sdept='CS') and sdept<>'CS'; --本查询也可以使用聚集函数来实现 select sname,sage from student where sage<(select max(sage) from student where sdept='CS') and sdept<>'CS'; --例5:查询非CS系中比CS系中所有学生年龄都小的学生的姓名和年龄 select sname,sage from student where sage<all(select sage from student where sdept='CS') and sdept<>'CS'; --本查询也可以使用聚集函数来实现 select sname,sage from student where sage<(select min(sage) from student where sdept='CS') and sdept<>'CS'; --实际上使用聚集函数来查询效率更高
④带有exists谓词的子查询
带有exists谓词的子查询不返回数据,只产生true或false,子查询的查询结果为非空,where子句返回true,否则返回false
带有not exists谓词的子查询不返回数据,只产生true或false,子查询的查询结果为空,where子句返回true,否则返回false
--例6:查询所有选修了一号课程的学生名字 select sname from student where exists(select * from sc where cno=1 and sc.sno=student.sno); --由exists引出的子查询,通常使用select *,因为给出列名无意义 --例1中的子查询是一个相关子查询 --例7:查询没有选修一号课程的学生名字 select sname from student where not exists(select * from sc where cno=1 and sc.sno=student.sno); --一些带有exists或not exists谓词的子查询不能被其他形式的子查询等价替换 --但带有in谓词,比较运算符,any和all谓词的子查询都能用带exists谓词的子查询等价替换 --例如带in谓词的例1就可以改为带exists谓词的子查询形式 select sno,sname,sdept from student s1 where exists(select * from student s2 where s2.sdept=s1.sdept and s2.sname='刘晨'); --因为带exists谓词的相关子查询只关心内层查询是否有返回值,并不需要查具体值,因此效率不一定低于不相关子查询 --例8:查询选修了全部课程的学生姓名 --SQL中没有全称量词(for all),但是可以把带有全称量词的谓词转换为等价的带有存在量词的谓词 --题目等价于:没有一门课程是他不选修的 select sname from student where not exists(select * from course where not exists(select * from sc where sc.sno=student.sno and sc.cno=course.cno)); --例9:查询至少选修了学生2选修的全部课程的学生号码 --题目等价于:不存在这样的课程y,学生2选修了y,而学生x没有选 select distinct sno from sc scx where not exists(select * from sc scy where scy.sno=2 and not exists(select * from sc scz where scz.sno=scx.sno and scz.cno=scy.cno));