子查詢是嵌套在一個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;
