SQL子查询

子查询是嵌套在一个select语句中的另一个select语句。当需要从一个表中检索信息,检索条件值又是来自该表本身的内部数据时,子查询非常有用。 
子查询可以嵌入以下SQL子句中:where子句、having子句和from子句。 

例:查询工资比编号为7566雇员工资高的雇员姓名。 
SQL>select ename 
from   emp 
where sal> 
      (select sal 
        from emp 
        where empno=7566) 
order by ename; 

说明: 
(1)子查询要用括号括起来; 
(2)将子查询放在比较运算符的右边; 
(3)不要在子查询中使用order by子句,select语句中只能有一个order by子句,并且它只能是主select语句的最后一个子句。  但是如果有top n 的话, 可以有order by.(例如分页查询中:select top 5 * from emp where empno in (select TOP (40) empno from emp order by empno desc) order by empno )

1、单行子查询 
内部select语句只返回一行结果的查询(单列)。主查询的where子句使用单行子查询返回结果要采用单行比较运算符(=、>、>=、<、<=、<>)。 

1.1 Where子句中使用单行子查询 

例:显示和雇员scott同部门的雇员姓名、工资和部门编号。 
SQL>select ename,sal,deptno 
from   emp 
where deptno= 
      (select deptno 
        from emp 
        where ename='SCOTT'); 

ENAME            SAL DEPTNO 
---------- --------- ------ 
SMITH         800.00     20 
JONES        2975.00     20 
SCOTT        3000.00     20 
ADAMS        1100.00     20 
FORD         3000.00     20 

练习:显示和雇员SCOTT从事相同工作,并且工资大于JAMES的雇员姓名、工作和工资。 
SQL>select ename,job,sal 
from   emp 
where job= 
      (select job from emp where ename='SCOTT') 
and   sal> 
      (select sal from emp where ename='JAMES'); 

ENAME      JOB             SAL 
---------- --------- --------- 
SCOTT      ANALYST     3000.00 
FORD       ANALYST     3000.00 

1.2单行子查询中使用组函数 

例:显示工资最低的雇员姓名、工作和工资。 
SQL> select ename,job,sal 
from emp 
where sal=(select min(sal) from emp); 

ENAME      JOB             SAL 
---------- --------- --------- 
SMITH      CLERK        800.00 

练习1:显示工资最高的雇员姓名、工作和工资。 
练习2:显示工资高于平均工资的雇员姓名、工作、工资和工资等级。 
SQL>select e.ename as 姓名, 
       e.job as 工作, 
       e.sal as 工资, 
       s.grade as 工资等级 
from emp e,salgrade s 
where e.sal>(select avg(sal) from emp)            
and e.sal between s.losal and s.hisal; 

姓名       工作             工资       工资等级 
---------- --------- --------- ---------- 
JONES      MANAGER     2975.00          4 
BLAKE      MANAGER     2850.00          4 
CLARK      MANAGER     2450.00          4 
SCOTT      ANALYST     3000.00          4 
KING       PRESIDENT   5000.00          5 
FORD       ANALYST     3000.00          4 

思考? 
e.sal>(select avg(sal) from emp)和e.sal between s.losal and s.hisal顺序对调查询效率上有何差异?为什么? 

1.3 having子句中使用单行子查询 

例:显示部门内最低工资比20部门最低工资要高的部门的编号及部门内最低工资。 
(1)按部门显示部门编号、部门最低工资 
SQL>select deptno as 部门编号, 
       min(sal) as 最低工资 
from emp 
group by deptno; 

(2)查询20部门最低工资 
select min(sal) from emp where deptno=20 

(3)使用having子句把(2)作为(1)的子查询 
SQL>select deptno as 部门编号, 
       min(sal) as 最低工资 
from emp 
group by deptno 
having min(sal)>(select min(sal) 
                 from emp 
                 where deptno=20); 

    部门编号  最低工资 
--------  ---------- 
      30        950 
  10       1300 


练习:查询平均工资最低的工种名称及其平均工资。 
(1)按工种查询平均工资 
SQL>select job,avg(sal) from emp group by job; 

(2)按工种查询最低平均工资 
SQL>select min(avg(sal)) from emp group by job; 

(3)使用having子句把(2)作为(1)子查询 
SQL>select job,avg(sal) 
from emp 
group by job 
having avg(sal)=(select min(avg(sal)) 
                 from emp 
                 group by job); 

JOB           AVG(SAL) 
---------    ---------- 
CLERK         1037.5 


问题思考: 
(1)当单行子查询返回的结果为null时,主查询是否正确? 

例:查询和SMITH从事相同工作的雇员姓名和工作 
select ename,job 
from emp 
where job=(select job 
           from emp 
           where ename='SMITHS'); 
如果SMITH误拼写成SMITHS则返回结果为null。 

(2)子查询中使用group by子句,主查询中是否可以使用单行比较符? 

例:下面的SQL语句能正确执行吗? 
SQL>select ename,job 
from emp 
where sal=(select min(sal) 
           from emp 
           group by deptno) 

ORA-01427: 单行子查询返回多个行 

2、多行子查询 
内部select语句返回多行结果,主查询的where子句使用多行子查询返回的结果要采用多行比较运算符,多行比较运算符可以和一个或多个值进行比较。 
多行运算比较符:in、any、all 

2.1 使用in运算符的多行子查询 
In运算符将等于列表中的任意一项。 

例1:查询有下属的雇员姓名、工作、工资和部门号。 
SQL>select ename,job,sal,deptno 
from emp 
where empno in (select mgr from emp); 

ENAME      JOB             SAL DEPTNO 
---------- --------- --------- ------ 
JONES      MANAGER     2975.00     20 
BLAKE      MANAGER     2850.00     30 
CLARK      MANAGER     1500.00     10 
SCOTT      ANALYST     3000.00     20 
KING       PRESIDENT   5000.00     10 
FORD       ANALYST     3000.00     20 

思考? 
如果要查询没有下属的雇员姓名、工作、工资和部门号,如下的SQL语句是否可以获得预期的结果?如果不能,应如何修改? 
SQL>select ename,job,sal,deptno 
from emp 
where empno not in (select mgr from emp); 

解答: 
为把问题说清楚,先看一下子查询的执行结果: 
SQL> select ename,mgr from emp; 

ENAME        MGR 
---------- ----- 
SMITH       7902 
ALLEN       7698 
WARD        7698 
JONES       7839 
MARTIN      7698 
BLAKE       7839 
CLARK       7839 
SCOTT       7566 
KING       
TURNER      7698 
ADAMS       7788 
JAMES       7698 
FORD        7566 
MILLER      7782 

子查询返回的结果中有一个mgr是空值,not in运算符将会用主查询条件(empno)与子查询中的每个结果(mgr)进行逻辑非的比较。因为子查询返回结果中有条空值,任何条件和空值比较都是空值。因此只要空值成为子查询的一部分,就不能用not in运算符。 

SQL语句更正如下: 
SQL>select ename,job,sal,deptno 
from emp 
where empno not in (select nvl(mgr,-1) from emp); 

或 
SQL>select ename,job,sal,deptno 
from emp 
where empno not in (select mgr from emp where mgr is not null); 

例2:查询各部门中工资最低的员工姓名、工作、工资和部门号 
SQL>select ename,job,sal,deptno 
from emp 
where sal in (select min(sal) 
          from emp 
          group by deptno); 

ENAME      JOB             SAL DEPTNO 
---------- --------- --------- ------ 
JAMES      CLERK        950.00     30 
SMITH      CLERK        800.00     20 
MILLER     CLERK       1300.00     10 

练习1:查询部门中工资最高的雇员姓名、工作、工资和部门号。 
SQL>select ename,job,sal,deptno 
from emp 
where sal in (select max(sal) 
              from emp 
          group by deptno); 

练习2:查询与销售部门(SALES)工作相同的其它部门雇员姓名、工作、工资和部门名称。 
SQL>select e.ename,e.job,e.sal,d.dname 
from emp e,dept d 
where e.deptno=d.deptno 
      and d.dname<>'SALES' 
      and job in (select distinct e.job 
              from emp e,dept d 
              where e.deptno=d.deptno and d.dname='SALES'); 

ENAME      JOB             SAL DNAME 
---------- --------- --------- -------------- 
CLARK      MANAGER     2450.00 ACCOUNTING 
JONES      MANAGER     2975.00 RESEARCH 
MILLER     CLERK       1300.00 ACCOUNTING 
ADAMS      CLERK       1100.00 RESEARCH 
SMITH      CLERK        800.00 RESEARCH 


2.2 使用any运算符的多行子查询 
Any运算符将和内部查询返回的结果逐个比较,与单行操作符配合使用。 
<any:表示比子查询返回结果中的最大值小; 
=any:表示可以是子查询返回结果中的任意一个值; 
>any:表示比子查询返回结果中的最小值大。 

例1:查询工资低于某个文员(CLERK)雇员工资,但不从事文员工作的雇员编号、姓名、工种和工资。 
SQL>select empno,ename,job,sal 
from emp 
where sal<any 
          (select sal 
           from emp 
           where job='CLERK') 
    and job<>'CLERK'; 

EMPNO ENAME      JOB             SAL 
----- ---------- --------- --------- 
7521 WARD       SALESMAN    1250.00 
7654 MARTIN     SALESMAN    1250.00 

例2:查询工资高于某个文员(CLERK)雇员工资,但不从事文员工作的雇员编号、姓名、工种和工资。 
SQL>select empno,ename,job,sal 
from emp 
where sal>any 
          (select sal 
           from emp 
           where job='CLERK') 
    and job<>'CLERK'; 

EMPNO ENAME      JOB             SAL 
----- ---------- --------- --------- 
7839 KING       PRESIDENT   5000.00 
7788 SCOTT      ANALYST     3000.00 
7902 FORD       ANALYST     3000.00 
7566 JONES      MANAGER     2975.00 
7698 BLAKE      MANAGER     2850.00 
7782 CLARK      MANAGER     2450.00 
7499 ALLEN      SALESMAN    1600.00 
7844 TURNER     SALESMAN    1500.00 
7521 WARD       SALESMAN    1250.00 
7654 MARTIN     SALESMAN    1250.00 

练习1:查询工资高于部门编号是30的部门内某个雇员工资,但不在该部门工作的雇员姓名、工种、工资和部门编号。 
SQL>select ename,job,sal,deptno 
from emp 
where sal>any 
          (select sal 
           from emp 
           where deptno=30) 
      and deptno<>30; 

练习2:查询工资低于部门名称是SALES的部门内某个雇员工资,但不在该部门工作的雇员姓名、工种、工资、部门编号和部门名称。 
SQL>select e.ename,e.job,e.sal,d.deptno,d.dname 
from emp e,dept d 
where e.deptno=d.deptno 
      and d.dname<>'SALES' 
      and sal<any 
             (select distinct e.sal 
              from emp e,dept d 
              where e.deptno=d.deptno and d.dname='SALES'); 

ENAME      JOB             SAL DEPTNO DNAME 
---------- --------- --------- ------ -------------- 
SMITH      CLERK        800.00     20 RESEARCH 
ADAMS      CLERK       1100.00     20 RESEARCH 
MILLER     CLERK       1300.00     10 ACCOUNTING 
CLARK      MANAGER     2450.00     10 ACCOUNTING 

2.3 使用all运算符的多行子查询 
All运算符将和内部查询返回的每个结果比较。 
>all:比最大的大; 
<all:比最小的小。 

例1:查询高于所有部门平均工资的雇员姓名、工作、工资和部门编号。 
SQL>select ename,job,sal,deptno 
from emp 
where sal>all (select avg(sal) 
               from emp 
               group by deptno); 

ENAME      JOB             SAL DEPTNO 
---------- --------- --------- ------ 
JONES      MANAGER     2975.00     20 
SCOTT      ANALYST     3000.00     20 
KING       PRESIDENT   5000.00     10 
FORD       ANALYST     3000.00     20 

例2:查询低于所有部门平均工资的雇员姓名、工作、工资和部门编号。 
SQL>select ename,job,sal,deptno 
from emp 
where sal<all (select avg(sal) 
               from emp 
               group by deptno); 

ENAME      JOB             SAL DEPTNO 
---------- --------- --------- ------ 
SMITH      CLERK        800.00     20 
WARD       SALESMAN    1250.00     30 
MARTIN     SALESMAN    1250.00     30 
TURNER     SALESMAN    1500.00     30 
ADAMS      CLERK       1100.00     20 
JAMES      CLERK        950.00     30 
MILLER     CLERK       1300.00     10 

练习1:查询工资高于部门编号为30的部门内所有员工工资的雇员姓名、工作、工资和部门编号 
select ename,job,sal,deptno 
from emp 
where sal>all (select sal from emp where deptno=30); 

ENAME      JOB             SAL DEPTNO 
---------- --------- --------- ------ 
JONES      MANAGER     2975.00     20 
SCOTT      ANALYST     3000.00     20 
KING       PRESIDENT   5000.00     10 
FORD       ANALYST     3000.00     20 

练习2:查询工资等级为4的雇员姓名、工作、工资、部门编号和工资等级,同时满足该雇员工资高于部门编号为30的部门内所有员工工资。 
SQL>select e.ename,e.job,e.sal,e.deptno,s.grade 
from emp e,salgrade s 
where s.grade=4 
and e.sal between s.losal and s.hisal 
and e.sal>all (select sal from emp where deptno=30); 

ENAME      JOB             SAL DEPTNO      GRADE 
---------- --------- --------- ------ ---------- 
JONES      MANAGER     2975.00     20          4 
SCOTT      ANALYST     3000.00     20          4 
FORD       ANALYST     3000.00     20          4 


3、多列子查询 
多列子查询返回多列结果的内部select语句,多列子查询中的列比较有成对比较与不成对比较两种方法。 
多列子查询分为成对比较多列子查询和非成对比较多列子查询。 

对emp表的数据进行修改: 
SQL> update emp set sal=1600,comm=300 where ename='SMITH'; 
SQL> update emp set sal=1500,comm=300 where ename='CLARK'; 

3.1成对比较多列子查询 
例:查询与部门编号为30的部门中任意一个雇员的工资和奖金完全相同的雇员姓名、工资、奖金、部门编号,满足该雇员不是来自30号部门。 
(1)查询30部门内雇员工资和奖金 
SQL>select sal,nvl(comm,-1) from emp where deptno=30;      

SAL NVL(COMM,-1) 
--------- ------------ 
  1600.00          300 
  1250.00          500 
  1250.00         1400 
  2850.00           -1 
  1500.00            0 
   950.00           -1 

(2)查询非30部门内雇员姓名、工资、奖金和部门编号 
SQL>select ename,sal,nvl(comm,-1),deptno from emp where deptno<>30; 

ENAME            SAL NVL(COMM,-1) DEPTNO 
---------- --------- ------------ ------ 
SMITH        1600.00          300     20 
JONES        2975.00           -1     20 
CLARK        1500.00          300     10 
SCOTT        3000.00           -1     20 
KING         5000.00           -1     10 
ADAMS        1100.00           -1     20 
FORD         3000.00           -1     20 
MILLER       1300.00           -1     10 

(3)把(1)作为(2)的子查询 
查询(2)中与查询(1)中工资和奖金完全匹配的只有SMITH一个雇员,下面找出该员工。 
SQL>select ename,sal,nvl(comm,-1),deptno 
from emp 
where deptno<>30 
      and (sal,nvl(comm,-1))  in (select sal,nvl(comm,-1) 
                                  from emp 
                                  where deptno=30); 

ENAME            SAL NVL(COMM,-1) DEPTNO 
---------- --------- ------------ ------ 
SMITH        1600.00          300     20 

练习1:创建一查询,显示能获得与SCOTT一样工资和奖金的其他雇员的姓名、受雇日期和工资。 

练习2:查询各部门中工资等级最高的雇员姓名、工作、工资、工资等级和部门号。 
select e.ename,e.job,e.sal,s.grade,e.deptno 
from emp e,salgrade s 
where e.sal between s.losal and s.hisal 
and (s.grade,e.deptno)  in ( 
                           select  max(s.grade),e.deptno 
                           from emp e,salgrade s 
                           where e.sal between s.losal and s.hisal 
                           group by e.deptno) 
order by e.deptno; 

ENAME      JOB             SAL      GRADE DEPTNO 
---------- --------- --------- ---------- ------ 
KING       PRESIDENT   5000.00          5     10 
FORD       ANALYST     3000.00          4     20 
JONES      MANAGER     2975.00          4     20 
SCOTT      ANALYST     3000.00          4     20 
BLAKE      MANAGER     2850.00          4     30 

练习3:查询各部门中工资等级最高的雇员姓名、工作、工资、工资等级和部门名称。 
select e.ename,e.job,e.sal,s.grade,d.dname 
from emp e,salgrade s,dept d 
where e.sal between s.losal and s.hisal 
      and e.deptno=d.deptno 
      and (s.grade,e.deptno)  in (select  max(s.grade),e.deptno 
                                 from emp e,salgrade s 
                                 where e.sal between s.losal and s.hisal 
                                 group by e.deptno) 
order by e.deptno; 

ENAME      JOB             SAL      GRADE DNAME 
---------- --------- --------- ---------- -------------- 
KING       PRESIDENT   5000.00          5 ACCOUNTING 
FORD       ANALYST     3000.00          4 RESEARCH 
JONES      MANAGER     2975.00          4 RESEARCH 
SCOTT      ANALYST     3000.00          4 RESEARCH 
BLAKE      MANAGER     2850.00          4 SALES 

3.2非成对比较多列子查询 
例:查询工资与30部门中任意一个雇员的工资相等,同时奖金也与30部门中任意一个雇员奖金相等的雇员姓名、工资、奖金、部门编号,但该雇员不是来自30号部门。 

SQL>select ename,sal,nvl(comm,-1),deptno 
from emp 
where deptno<>30 
      and sal in (select sal 
                  from emp 
                  where deptno=30) 
      and nvl(comm,-1) in (select nvl(comm,-1) 
                           from emp 
                           where deptno=30); 

ENAME            SAL NVL(COMM,-1) DEPTNO 
---------- --------- ------------ ------ 
SMITH        1600.00          300     20 
CLARK        1500.00          300     10 


练习1:查询工资与销售部门(SALES)中任意一个雇员的工资相等,同时奖金也与该部门中任意一个雇员奖金相等的雇员姓名、工资、奖金、部门编号、部门名称,但该雇员不是来自销售部门。 
SQL>select e.ename,e.sal,nvl(e.comm,-1),d.deptno,d.dname 
from emp e,dept d 
where (e.deptno=d.deptno) and d.dname<>'SALES' 
      and e.sal in 
(select e.sal 
                 from emp e,dept d 
                 where (e.deptno=d.deptno) and d.dname='SALES') 
      and nvl(e.comm,-1) in 
(select nvl(e.comm,-1) 
                         from emp e,dept d 
                         where (e.deptno=d.deptno) and d.dname='SALES'); 

ENAME            SAL NVL(E.COMM,-1) DEPTNO DNAME 
---------- --------- -------------- ------ -------------- 
CLARK        1500.00            300     10 ACCOUNTING 
SMITH        1600.00            300     20 RESEARCH 

练习2:显示与工作在DALLAS的雇员的工资及奖金同时匹配的雇员姓名、部门名称及工资。 


3、相关子查询(exists) 
相关子查询指需要引用主查询列表的子查询语句,通过exists谓词实现。对主查询的每条记录都需执行一次子查询来测试是否匹配。若子查询返回结果非空,则主查询的where子句返回值为true,否则返回值为false。 

例:查询在纽约(NEW YORK)工作的雇员姓名、工种、工资和奖金。 
SQL>select ename,job,sal,comm 
from emp 
where exists (select * 
              from dept 
              where emp.deptno=deptno and loc='NEW YORK'); 

ENAME      JOB             SAL      COMM 
---------- --------- --------- --------- 
CLARK      MANAGER     1500.00    300.00 
KING       PRESIDENT   5000.00 
MILLER     CLERK       1300.00 


练习:查询工资等级为4的雇员姓名、工种和工资 
SQL>select ename,job,sal 
from emp 
where exists (select * 
              from  salgrade 
              where emp.sal between losal and hisal 
                and grade=4); 

ENAME      JOB             SAL 
---------- --------- --------- 
FORD       ANALYST     3000.00 
SCOTT      ANALYST     3000.00 
JONES      MANAGER     2975.00 
BLAKE      MANAGER     2850.00 


4、from子句中使用子查询 
在from子句中使用子查询时,必须给子查询指定别名。 

例:显示工资高于部门平均工资的雇员姓名、工作、工资和部门号。 
SQL>select ename,job,sal,emp.deptno 
from emp, (select deptno,avg(sal) avgsal 
           from  emp 
           group by deptno)s 
where emp.deptno=s.deptno and sal>s.avgsal; 

ENAME      JOB             SAL DEPTNO 
---------- --------- --------- ------ 
ALLEN      SALESMAN    1600.00     30 
JONES      MANAGER     2975.00     20 
BLAKE      MANAGER     2850.00     30 
SCOTT      ANALYST     3000.00     20 
KING       PRESIDENT   5000.00     10 
FORD       ANALYST     3000.00     20 

练习:查询各部门中工资等级最高的雇员姓名、工作、工资、工资等级和部门号。 
方法一、from子句中使用一个子查询 
select e.ename,e.job,e.sal,s.grade,e.deptno 
from emp e, 
     salgrade s, 
     (select max(s.grade) grade,e.deptno 
      from emp e,salgrade s 
      where e.sal between s.losal and s.hisal 
      group by e.deptno)q 
where  e.sal between s.losal and s.hisal 
       and e.deptno=q.deptno and s.grade=q.grade 
order by e.deptno; 

ENAME      JOB             SAL      GRADE DEPTNO 
---------- --------- --------- ---------- ------ 
KING       PRESIDENT   5000.00          5     10 
JONES      MANAGER     2975.00          4     20 
SCOTT      ANALYST     3000.00          4     20 
FORD       ANALYST     3000.00          4     20 
BLAKE      MANAGER     2850.00          4     30 

方法二、from子句中使用两个子查询 
select p.ename,p.job,p.sal,p.grade,p.deptno 
from  ( 
         select e.ename,e.job,e.sal,s.grade,e.deptno 
         from emp e,salgrade s 
         where e.sal between s.losal and s.hisal)p, 
      ( 
         select max(s.grade) grade,e.deptno 
         from emp e,salgrade s 
         where e.sal between s.losal and s.hisal 
         group by e.deptno)q 
where p.deptno=q.deptno and p.grade=q.grade 
order by p.deptno; 

 
posted on 2012-08-03 13:14  duanxz  阅读(5144)  评论(1编辑  收藏  举报