4.2.6 子查询

4.2.6 子查询

子查询指一个查询语句嵌套在另一个查询语句内部的查询,这个特性从MySQL 4.1开始引入。在select子句中,先计算子查询,子查询的结果作为外层另一个查询的过滤条件,查询可以基于一个表或者多个表。子查询中常用的操作符有any(some)、all、in、exists。子查询可以添加到select、update和delete语句中,而且可以进行多层嵌套。子查询中也可以使用比较运算符,如“<”“<=”“>”“>=”和“!=|<>”等。本节将介绍如何在select语句中嵌套子查询。

1.带any、some关键字的子查询

any和some关键字是同义词,表示满足其中任一条件,它们允许创建一个表达式对子查询的返回值列表进行比较,只要满足内层子查询中的任何一个比较条件,就返回一个结果作为外层查询的条件。

下面定义两个表tab1和tab2:

分别向两个表中插入数据:

any关键字跟在一个比较操作符的后面,表示若与子查询返回的任何值比较为true,则返回true。

【例4.63】返回tab2表的所有n2字段,然后将tab1中的n1的值与之进行比较,只要大于n2的任何一个值,即为符合查询条件的结果。

SQL语句及执行效果如下:

在子查询中,返回的是tab2表的所有n2字段的结果(6,4,11,20),然后将tab1中的n1字段的值与之进行比较,只要大于n2字段的任意一个数,即为符合条件的结果。

2.带all关键字的子查询

all关键字与any和some不同,使用all时,需要同时满足所有内层查询的条件。例如修改例4.63,用all关键字替换any。

all关键字跟在一个比较操作符的后面,表示与子查询返回的所有值比较为true,则返回true。

【例4.64】返回tab1表中比tab2表n2字段所有值都大的值。

SQL语句及执行效果如下:

在子查询中,返回的是tab2的所有n2字段的结果(6,4,11,20),然后将tab1中的n1字段的值与之进行比较,大于所有n2字段值的n1值只有27,因此返回结果为27。

3.带exists关键字的子查询

exists关键字后面的参数是一个任意的子查询,系统对子查询进行运算来判断它是否返回行,如果至少返回一行,那么exists的结果为true,此时外层的查询语句将进行查询;如果子查询没有返回任何行,那么exists返回的结果是false,此时外层语句不进行查询。

【例4.65】查询employees表中是否存在职位ejob等于‘程序员’的员工信息,如果存在,则查询departments表中的记录。

SQL语句及执行效果如下:

由结果可以看到,内层查询结果表明employees表中存在ejob等于‘程序员’的记录,因此exists表达式返回true;外层查询语句接收true之后对表departments进行查询,返回该表所有的记录。

exists关键字可以和条件表达式一起使用。

【例4.66】查询departmentss表中是否存在部门名称dname等于‘财务部’的部门,如果存在,则查询employees表中的esal大于15 000的记录。

SQL语句及执行效果如下:

由结果可以看到,内层查询结果表明departments表中存在dname=的记录,因此exists表达式返回true;外层查询语句接收true之后,根据查询条件esal>15 000对employees表进行查询,返回结果为一条记录。

not exists与exists使用方法相同,返回的结果相反。子查询如果至少返回一行,那么not exists的结果为false,此时外层查询语句将不进行查询;如果子查询没有返回任何行,那么not exists返回的结果是true,此时外层语句将进行查询。

【例4.67】查询departmentss表中是否存在部门名称dname等于‘财务部’的部门,如果不存在,则查询employees表中的记录。

SQL语句及执行效果如下:

查询语句“select*from departmentswhere dname=”,对departments表进行查询,返回了一条记录,not exists表达式返回false,外层表达式接收false,将不再查询employees表中的记录。

提示:ex i s t s和no t ex i s t s的结果只取决于是否会返回行,而不取决于这些行的内容,所以这个子查询输出列表通常是无关紧要的。

4.带in关键字的子查询

in关键字进行子查询时,内层查询语句仅仅返回一个数据字段,这个数据字段里的值将提供给外层查询语句进行比较操作。

【例4.68】查询employees表中某些员工的姓名、工资、奖金、部门编号,条件是他们的工资等于部门编号为3中任何一个员工的工资,但是不显示部门编号为3的员工信息。

SQL语句及执行效果如下:

查询语句“selectesal from employeeswhere deptno=3”返回部门编号为3的员工的工资,单独执行内查询,查询结果如下:

可以看到,部门编号为3的员工工资有12 000、7 000、6 000、6 500、4 000,然后执行外层查询。在employees表中,查询员工工资与部门编号为3的员工工资相等的只有6 000或4 000。所以嵌套子查询语句还可以写成如下形式,实现相同的效果:

由上面查询可以看到,结果中有deptno为3的员工信息,但例4.68要求不显示部门编号为3的员工信息,所以例4.68在查询的末尾要加“deptno<>3”这个条件语句。

这个例子说明在处理select语句的时候,MySQL实际上执行了两个操作过程,即先执行内层子查询,再执行外层查询,内层子查询的结果作为外部查询的比较条件。

select语句中可以使用not in关键字,其作用与in正好相反。

【例4.69】查询employees表中员工工资与部门编号为1、2、3的员工工资不等的员工信息。

SQL语句及执行效果如下:

这里返回的结果有2条记录,由前面可以看到,子查询查询出部门编号为1、2、3的员工工资,并且使用distinct关键字去掉重复行,单独执行子查询。查询结果如下:

可以看到例4.69查询结果中esal字段的值不在其子查询范围内,满足题目要求。

提示:子查询的功能也可以通过连接查询完成,但是子查询使得MySQL代码更容易阅读和编写。

5.带比较运算符的子查询

在前面介绍带any、all关键字的子查询时,使用了“>”比较运算符,子查询时还可以使用其他的比较运算符,如“<”“<=”“>”“>=”“=”“!=”等。

【例4.70】查询employees表中部门名称为“人事部”的员工信息。

SQL语句及执行结果如下:

该嵌套查询首先在departments表中查找dname等于“人事部”的部门编号dno,单独执行子查询查看dno的值,执行下面的操作过程:

然后在外层查询时,在employees表中查找deptno为4的员工信息,结果表明,deptno为4的员工一共有四名,分明为“李丽”“邵强”“吴坤”“马萍”。

【例4.71】查询employees表中部门名称不是“人事部”的员工信息。

SQL语句及执行效果如下:

该嵌套查询执行过程与前面相同,在这里使用了不等于“<>”运算符,因此返回的结果和前面正好相反。

实训:

①查询至少有一个员工的所有部门。(提示:部门编号在员工表中存在。)

②查询工资高于公司平均工资的所有员工。

③查询所有比部门编号为3的员工工资高的员工信息。

④找出比部门编号为1的员工中任何一个工资高的员工姓名和工资。(提示:只要比部门编号为1的员工中那个工资最少的员工工资高就可以。)

⑤查询与“杨幂”从事相同工作的所有员工。