连接查询:同时涉及两个以上的表的查询
连接条件:用来连接两个表的条件
[<表名1>.]<列名1> <比较运算符> [<表名2>.]<列名2>
例:Student.Sno = SC.Sno
连接字段:连接条件中的列名称
如:Sno为上面例子中的连接字段
注意:连接字段类型必须是可比的,但名字不必相同
等值连接:连接运算符为=
[例 3.49] 查询每个学生及其选修课程的情况
SELECT Student.*, SC.*
FROM Student, SC
WHERE Student.Sno = SC.Sno;
或
SELECT *
FROM Student,SC
WHERE Student.Sno = SC.Sno;
若代码为
SELECT Student.*, SC.*
FROM Student, SC
则显示77个项目
已知Student中有11行
SC中有7行
由此可见代码即两表的笛卡尔积,11*7=77
自然连接
[例 3.50] 对[例 3.49]用自然连接完成。
SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
FROM Student,SC
WHERE Student.Sno = SC.Sno;
[例 3.51 ]查询选修2号课程且成绩在90分以上的所有学生的学号和姓名。
SELECT Student.Sno,Sname
FROM Student, SC
WHERE Student.Sno=SC.Sno AND SC.Cno='2' AND SC.Grade>86;
一条SQL语句可以同时完成选择和连接查询。先从SC中选择出Cno='2’并且Grade>90的元组形成一个中间关系,再和Student中满足连接条件的元组进行连接得到最终的结果关系
注:因为两个表中都有Sno,所以必须在前面写表名。而Cno和Grade只有SC表中有,不写表名也是可以的,即
SELECT Student.Sno,Sname
FROM Student, SC
WHERE Student.Sno=SC.Sno AND Cno='2' AND Grade>86;
自身连接:
一个表与其自己进行连接需要给表起别名以示区别
所有属性名都是同名属性,因此必须使用“别名”
[例 3.52]查询每一门课的间接先修课(即先修课的先修课)
SELECT FIRST_TABLE.Cno, SECOND_TABLE.Cpno
FROM Course FIRST_TABLE, Course SECOND_TABLE
WHERE FIRST_TABLE.Cpno = SECOND_TABLE.Cno;
将一张表看成两张相同的表,再进行WHERE条件查询
注:标准SQL不显示空值,即无3、5行
外连接与普通连接的区别
普通连接操作只输出满足连接条件的元组
外连接操作以指定表为连接主体,将主体表中不满足连接条件的元组一并输出
左外连接
列出左边关系中所有的元组
右外连接
列出右边关系中所有的元组
[例 3. 53] 改写[例 3.49]
SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
FROM Student LEFT OUTER JOIN SC ON (Student.Sno=SC.Sno);
与3.49对比
可以看到3.49只显示选课的同学,而左外连接后会显示所有学生,即LEFT左边Student中所有的元组
右外连接即LEFT改为RIGHT,显示RIGHT右边SC中所有的元组
SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
FROM Student RIGHT OUTER JOIN SC ON (Student.Sno=SC.Sno);
外连接只输出满足连接条件的元组
SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
FROM Student inner JOIN SC ON (Student.Sno=SC.Sno);
多表连接:两个以上的表进行连接
[例3.54]查询每个学生的学号、姓名、选修的课程名及成绩
SELECT Student.Sno, Sname, Cname, Grade
FROM Student, SC, Course
WHERE Student.Sno = SC.Sno AND SC.Cno = Course.Cno;
SELECT *
FROM Student, SC, Course
WHERE Student.Sno = SC.Sno AND SC.Cno = Course.Cno;
学号可以来自Student或SC,姓名只能来自Student,选修的课程只能来自Course,成绩只能来自SC,所以进行多表连接
嵌套查询
一个SELECT-FROM-WHERE语句称为一个查询块
将一个查询块嵌套在另一个查询块的WHERE子句或HAVING短语的条件中的查询称为嵌套查询
SELECT * /*外层查询/父查询*/
FROM Student
WHERE Sno IN
(SELECT Sno /*内层查询/子查询*/
FROM SC
WHERE Cno= '2');
SQL语言允许多层嵌套查询:即一个子查询中还可以嵌套其他子查询
子查询的限制:不能使用ORDER BY子句
不相关子查询:子查询的查询条件不依赖于父查询
由里向外 逐层处理。即每个子查询在上一级查询处理之前求解,子查询的结果用于建立其父查询的查找条件。
相关子查询:子查询的查询条件依赖于父查询
首先取外层查询中表的第一个元组,根据它与内层查询相关的属性值处理内层查询,若WHERE子句返回值为真,则取此元组放入结果表,然后再取外层表的下一个元组。重复这一过程,直至外层表全部检查完为止
[例 3.55] 查询与“刘晨”在同一个系学习的学生。
第一种方法
① 确定“刘晨”所在系名
SELECT Sdept
FROM Student
WHERE Sname= '刘晨';
结果为: CS
② 查找所有在CS系学习的学生。
SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept= 'CS';
第二种方法(将第一步查询嵌入到第二步查询的条件中)
--不相关子查询
SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept IN
(SELECT Sdept
FROM Student
WHERE Sname= '刘晨');
注:由于一个学生只可能在一个系学习,这里可以用=代替IN,但如果学生和系是一对多的关系,则不可代替
此时报错:子查询返回的值不止一个。当子查询跟随在 =、!=、<、<=、>、>= 之后,或子查询用作表达式时,这种情况是不允许的。
第三种方法:用自身连接完成[例 3.55]查询要求
SELECT S1.Sno, S1.Sname,S1.Sdept
FROM Student S1,Student S2
WHERE S1.Sdept = S2.Sdept AND S2.Sname = '刘晨';
先将同系的都连接起来再找与刘晨连接的系
第二种嵌套连接相对更简单一些
[例 3.56]查询选修了课程名为“信息系统”的学生学号和姓名
--不相关子查询
SELECT Sno,Sname
FROM Student
WHERE Sno IN
(SELECT Sno
FROM SC
WHERE Cno IN
(SELECT Cno
FROM Course
WHERE Cname= '信息系统'
)
);
① 首先在Course关系中找出“信息系统”的课程号,为3号
② 然后在SC关系中找出选修了3号课程的学生学号
③ 最后在Student关系中取出Sno和Sname
用连接查询实现[例 3.56] :
SELECT Student.Sno,Sname
FROM Student,SC,Course
WHERE Student.Sno = SC.Sno AND
SC.Cno = Course.Cno AND
Course.Cname='信息系统';
[例 3.57 ]找出每个学生超过他选修课程平均成绩的课程号。
--相关子查询
SELECT Sno, Cno
FROM SC x
WHERE Grade >=(
SELECT AVG(Grade)
FROM SC y
WHERE y.Sno=x.Sno
);
父查询的值在子查询中用到了(x)
过程:
从外层查询中取出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;
就是将一个学生的平均值送出再与自身各科成绩比较
使用ANY或ALL谓词时必须同时使用比较运算
语义为:
ANY 大于子查询结果中的某个值(大于最小值)
ALL 大于子查询结果中的所有值(大于最大值)
< ANY 小于子查询结果中的某个值(小于最大值)
< ALL 小于子查询结果中的所有值(小于最小值)
= ANY 大于等于子查询结果中的某个值(大于等于最小值)
= ALL 大于等于子查询结果中的所有值(大于等于最大值)
[例 3.58] 查询非计算机科学系中比计算机科学系任意一个学生年龄小的学生姓名和年龄
SELECT Sname,Sage
FROM Student
WHERE Sage < ANY
(SELECT Sage
FROM Student
WHERE Sdept= 'CS')
AND Sdept <> 'CS'; /*父查询块中的条件 */
1、首先处理子查询,找出CS系中所有学生的年龄,构成一个集合(20,19,18)
2、处理父查询,找所有不是CS系且年龄小于20或19或18的学生
或用聚集函数
SELECT Sname,Sage
FROM Student
WHERE Sage <
(SELECT MAX(Sage)
FROM Student
WHERE Sdept= 'CS')
AND Sdept <> 'CS';
1、首先处理子查询,找出CS系中所有学生的年龄的最大值20
2、处理父查询,找所有不是CS系且年龄小于20的学生
[例 3.59] 查询非计算机科学系中比计算机科学系所有学生年龄都小的学生姓名及年龄。
SELECT Sname,Sage
FROM Student
WHERE Sage < ALL
(SELECT Sage
FROM Student
WHERE Sdept= 'CS')
AND Sdept <> 'CS'; /*父查询块中的条件*/
1、首先处理子查询,找出CS系中所有学生的年龄,构成一个集合(20,19,18)
2、处理父查询,找所有不是CS系且年龄小于20和19和18的学生
或用聚集函数
SELECT Sname,Sage
FROM Student
WHERE Sage <
(SELECT MIN(Sage)
FROM Student
WHERE Sdept= 'CS')
AND Sdept <> 'CS';
1、首先处理子查询,找出CS系中所有学生的年龄的最小值18
2、处理父查询,找所有不是CS系且年龄小于18的学生
用时:两个小时,相对上节课内容来说较难理解,需要时间消化。理解之后觉得还可以,自己上手敲是很有必要的