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; 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM