子查询 = 简单查询 + 限定查询 + 多表查询 + 统计查询的综合体;
在之前强调过多表查询不建议大家使用,因为性能很差,但是多表查询最有利的替代者就是子查询,所以子查询在实际的开发之中使用的相当的多;
所谓的子查询指的就是在一个查询之中嵌套了其他的若干查询,嵌套子查询之后的查询SQL语句如下:
SELECT [DISTINCT] *|分组字段1 [别名] [,分组字段2 [别名] ,…] | 统计函数 ,( SELECT [DISTINCT] *|分组字段1 [别名] [,分组字段2 [别名] ,…] | 统计函数 FROM 表名称 [别名], [表名称 [别名] ,…] [WHERE 条件(s)] [GROUP BY 分组字段1 [,分组字段2 ,…]] [HAVING 分组后的过滤条件(可以使用统计函数)] [ORDER BY 排序字段 ASC | DESC [,排序字段 ASC | DESC]]) FROM 表名称 [别名], [表名称 [别名] ,…] ,( SELECT [DISTINCT] *|分组字段1 [别名] [,分组字段2 [别名] ,…] | 统计函数 FROM 表名称 [别名], [表名称 [别名] ,…] [WHERE 条件(s)] [GROUP BY 分组字段1 [,分组字段2 ,…]] [HAVING 分组后的过滤条件(可以使用统计函数)] [ORDER BY 排序字段 ASC | DESC [,排序字段 ASC | DESC]]) [WHERE 条件(s) ( SELECT [DISTINCT] *|分组字段1 [别名] [,分组字段2 [别名] ,…] | 统计函数 FROM 表名称 [别名], [表名称 [别名] ,…] [WHERE 条件(s)] [GROUP BY 分组字段1 [,分组字段2 ,…]] [HAVING 分组后的过滤条件(可以使用统计函数)] [ORDER BY 排序字段 ASC | DESC [,排序字段 ASC | DESC]])] [GROUP BY 分组字段1 [,分组字段2 ,…]] [HAVING 分组后的过滤条件(可以使用统计函数)] [ORDER BY 排序字段 ASC | DESC [,排序字段 ASC | DESC]];
理论上子查询可以出现在查询语句的任意位置上,但是从个人而言,子查询出现在WHERE和FROM子句之中较多;
以下的使用特点为个人总结,不是官方声明的:
- WHERE:子查询一般只返回单行列、多行单列、单行多列的数据;
- FROM:子查询返回的一般是多行的数据,当作一张临时表出现。
范例:要求查询出工资比SMITH还要高的全部雇员信息
要想完成本程序,首先必须要知道SMITH的工资是多少:
SELECT sal FROM emp WHERE ename='SMITH';
由于此时返回的是单列的数据,所以这个子句查询可以在WHERE中出现。
SELECT * FROM emp WHERE sal>( SELECT sal FROM emp WHERE ename='SMITH');
范例:要求查询出高于公司平均工资的全部雇员信息
公司的平均工资应该使用AVG()函数求出。
SELECT AVG(sal) FROM emp;
此时数据的返回结果是单行单列的数据,在WHERE之中出现。
SELECT * FROM emp WHERE sal>( SELECT AVG(sal) FROM emp);
以上所返回的是单行单列,但是在子查询之中,也可以返回单行多列的数据,只是这种子查询很少出现。
范例:子查询返回单行多列数据
SELECT * FROM emp WHERE (job,sal)=( SELECT job,sal FROM emp WHERE ename='ALLEN');
如果现在的子查询返回的是多行单列数据的话,这个时候就需要使用三种判断符判断了:IN、ANY、ALL;
1、 IN操作符:用于指定一个子查询的判断范围
这个操作符的使用实际上与之前讲解的IN是一样的,唯一不同的是,里面的范围由子查询指定了。
SELECT * FROM emp WHERE sal in ( SELECT sal FROM emp WHERE job='MANAGER');
但是在使用IN的时候还要注意NOT IN的问题,如果使用NOT IN操作,在子查询之中,如果有一个内容是null,则不会查询出任何的结果。
2、 ANY操作符:与每一个内容想匹配,有三种匹配形式
- =ANY:功能与IN操作符是完全一样的;
SELECT * FROM emp WHERE sal=ANY ( SELECT sal FROM emp WHERE job='MANAGER');
- >ANY:比子查询中返回记录最小的还要大的数据;
SELECT * FROM emp WHERE sal>ANY ( SELECT sal FROM emp WHERE job='MANAGER');
- <ANY:比子查询中返回记录的最大的还要小;
SELECT * FROM emp WHERE sal<ANY ( SELECT sal FROM emp WHERE job='MANAGER');
3、 ALL操作符:与每一个内容相匹配,有两种匹配形式:
- >ALL:比子查询中返回的最大的记录还要大
SELECT * FROM emp WHERE sal>ALL ( SELECT sal FROM emp WHERE job='MANAGER');
- <ALL:比子查询中返回的最小的记录还要小
SELECT * FROM emp WHERE sal<ALL ( SELECT sal FROM emp WHERE job='MANAGER');
以上的所有子查询都是在WHERE子句中出现的,那么下面再来观察在FROM子句中出现的查询,这个子查询一般返回的是多行多列的数据,当作一张临时表的方式来处理。
范例:查询出每个部门的编号、名称、位置、部门人数、平均工资
- 回顾:最早的时候使用的是多字段分组统计完成的:
SELECT d.deptno,d.dname,d.loc,COUNT(e.empno),AVG(e.sal) FROM emp e,dept d WHERE e.deptno(+)=d.deptno GROUP BY d.deptno,d.dname,d.loc;
这个时候实际上是产生了笛卡尔积,一共产生了56条记录;
- 新的解决方案:通过子查询完成,所有的统计查询只能在GROUP BY中出现,所以在子查询之中负责统计数据,而在外部的查询之中,负责将统计数据和dept表数据相统一。
SELECT d.deptno,d.dname,d.loc,temp.count,temp.avg FROM dept d,( SELECT deptno dno,COUNT(empno) count,AVG(sal) avg FROM emp GROUP BY deptno) temp WHERE d.deptno=temp.dno(+);
现在的程序中所操作的数据量:
- 子查询中统计的记录是14条记录,最终统计的显示结果是3条记录;
- dept表之中一共有4条记录;
- 如果现在产生笛卡尔积的话只有12条记录,再加上雇员的14条记录,一共才26条记录;
通过如上的分析,可以发现,使用子查询的确要比使用多表查询更加节省性能,所以在开发之中子查询出现是最多的,而且在给出一个不成文的规定:大部分情况下,如果最终的查询结果之中需要出现SELECT子句,但是又不能直接使用统计函数的时候,就在子查询中统计信息,即:有复杂统计的地方大部分都需要子查询。