数据查询是数据库的核心操作。SQL语言允许多层嵌套查询,使用户可以用多个简单查询构成复杂查询,增强SQL的查询能力。相关子查询和不相关子查询是最基本的子查询的形式。
需要学习SOL子查询的其它用法。
子查询的用法非常丰富,首先学习带有比较运算符的子查询和带有 ANY 或 ALL 谓词的子查询。
一、带有比较运算符的子查询
当能确切的知道内层子查询返回的是单值时,可以用比较运算符代替IN。
例1:查询和’王红”相同性别的学生学号和姓名。
①用自身连接:
Select Y.sno, Y.sname
From S X, S Y
Where X.sex=Y.sex and X.sname='王红’;
例1的查询可以使用自身连接查询实现。使用S表和S表的自身连接,要分别取名为X和Y,条件是“X表的性别和Y表的性别是相同的”、“X的姓名是王红”,即X的名字是王红时,将X的性别赋给Y的性别,输出的是Y的学号和姓名。
②用子查询:
Select sno,sname
From S
Where sex= (Select sex
From S
Where sname='王红’);
使用子查询实现时,首先计算出王红的性别,然后将它作为父查询的条件。
在S表中,先获得王红同学的性别,其性别为单值,因此可以在外查询的S表中用“性别等于王红同学的性别”为条件,然后输出和王红相同性别的学生的学号和姓名。
只要子查询的结果是单值,就可以用等号、大于号、小于号、大于等于、小于等于、不等于等等比较运算符来代替集合的谓词IN。
二、带有 ANY 或 ALL 谓词的子查询
(1)any:
<表达式><比较运算符>any<子查询结果集>
表示与集合中某个成员的比较关系;
(2)all:
<表达式><比较运算符>all <子查询结果集>
表示与集合中全体成员的比较的结果;
ANY 或 ALL 谓词与比较运算符联合使用,可以表达更丰富的语义。
例如:
age>all(13,15,17):
(13,15,17)是一个子查询的结果,是三个年龄值,需要判断的是年龄大于后面的结果集中的全部的值;如果年龄是16,则会返回假;若年龄是18,比结果集中的所有的值都大,则返回真。
age>any(13,15,17):
any谓词则表示只要大于结果集中的任何一个、某一个,就返回真;若年龄为14,则返回真;若年龄为12,则返回假。
all谓词的用法可以看做大于结果集中的最大值即为真;any谓词的用法可以看做大于结果集中的最小值即为真。
例2:查询选修c2课程的学生学号和姓名。
①用in :
Select sno,sname
From S
Where sno in
(Select sno
From SC
Where cno= 'c2');
可以使用不相关子查询实现。先在SC表中把“选了课程号是c2”的学生的学号获得,然后到S表中输出学生的学号以及姓名。
②用any或all:
Select sno,sname
From S
Where sno = any
(Select sno
From SC
Where cno= 'c2');
选了c2课程的任何一个学生都会被输出,所以子查询的结果是选了c2这门课程的学生的学号,即所有选了c2课程的学生的学号,因此在S表中去查找只要学号是它“选了c2课程的学生的学号”其中的一个,就返回真,被输出。所以使用谓词any实现。
例3:查询没选修c2课程的学生学号和姓名。
①用 not in :
Select sno,sname
From S
Where sno not in
(Select sno
From SC
Where cno='c2’);
首先使用不相关子查询实现。子查询获得的是“选了课程号是c2”的学生的学号,在S表中去查找只要学号不在子查询的结果集中,即没有选课程c2的学生被输出。
②用any或all:
Seleist sno,sname
From S
Where sno <>all
(Select sno
From SC
Where cno='c2’);
子查询获得的结果是“选了课程号是c2”的学生的学号的集合,要输出的是没有选择课程c2的学生,即与子查询的结果集中的任何一个都不相等的值才能被输出。
例4:查询男同学中比某一女生年龄小的学生姓名和年龄。
①用any:
Select sname,age
From S
Where sex='男’ and
age<any (Select age
From S
Where sex='女’);
在子查询中,得到的结果是“女生的所有的年龄”的集合,需要在S表中去查找男生的姓名和年龄,而他的年龄要比女生的所有年龄中的某一个小即可,因此,可以使用“<any”实现。
②用聚合函数:
Select sname,age
From S
Where sex='男 ’and
age<( Select max(age)
From S
Where sex='女’);
例4也可以使用聚集函数实现。比其中的某一个小,意味着只要比最大值小,就可以肯定比其中的某一个都小。在子查询中得到女生的年龄的最大值,然后在S表中去查找是男生、且年龄要比女生的年龄的最大值小的即为真。
例5:查询男生中比所有女生年龄都小的学生姓名和年龄。
①用all:
Select sname,age
From S
Where sex='男’ and
age< all (Select age
From s
Where sex='女’);
子查询的结果是“女生的所有的年龄”的集合,需要查找的是男生中比她们所有的女生的年龄都小的男生的姓名和年龄,因此使用“<all”实现。
②用聚合函数:
Select sname,age
From S
Where sex='男’ and
age< (Select min(age)
From S
Where sex='女’);
例5也可以使用聚集函数实现。比其中的任何一个小,意味着要比最小值小,就可以肯定比其中的任何一个都小。在子查询中得到女生的年龄的最小值,然后在S表中去查找是男生、且年龄要比女生的年龄的最小值小的即为真,可以被输出。
例6:查询平均分最高的学生学号和平均分。
不能用max(avg(grade)) ,将子查询结果用于比较。
Select sno , avg(grade)
From SC
Group by sno
Having avg(grade)>=all
(Select avg(grade)
From SC
Group by sno);
查找平均分最高,不能使用聚集函数的嵌套来写,即不能求完平均再求最高,这样的语法是错误的。
可以将子查询放在组条件子句Having子句后面来使用。
首先,外层的查询是在SC表中去对每一个学号进行分组、获得每一个学生的平均成绩;但是并不是所有的学生的平均成绩都输出,只需要平均成绩最高的学生的学号和平均分;因此,需要在Having子句中看到平均成绩需要满足的一个条件;子查询的结果是在SC表中得到了所有的学生的平均成绩的一个集合,需要最高平均分意味着要比所有的平均成绩都大,因此需要使用“>=all”实现。
综上所述,子查询适合的情况就是将子查询的结果用于比较。例如比较平均分最高、低于平均分的学生、低于平均工资的员工等等情况,就非常适合使用子查询实现。
三、总结
查询涉及多个表时,使用子查询层次清楚、易于构造、具有结构化程序设计的优点。
实现同一个查询可以有多种方法,不同的方法的执行效率可能会有差别。
子查询的用法是非常丰富的,如子查询也可以放在SELECT子句后面、或者放在FROM子句后面等等。