SQL Fundamentals || Oracle SQL語言
1、認識子查詢
2、WHERE子句中使用子查詢
3、在HAVING子句中使用子查詢
4、在FROM子句中使用子查詢
5、在SELECT子句中使用子查詢
6、WITH子句
子查詢(進階)
8、行列轉換
9、設置數據層次
一、認識子查詢
- 子查詢就是指的在一個完整的查詢語句之中,嵌套若干個不同功能的小查詢,從而一起完成復雜查詢(復雜查詢=限定查詢+多表查詢+統計查詢+子查詢)的一種編寫形式,為了讓讀者更加清楚子查詢的概念。
- 一個查詢語句內部可以定義多個子查詢;
- 子查詢一般在WHERE、FROM、HAVING子句之中出現較多,也可以在SELECT子句中出現.
例子1、查詢公司中工資最低的雇員的完整信息
分析:
1.查詢最低工資:SELECT MIN(sal) FROM emp;
2.查詢等於最低工資的雇員的信息:
SELECT * FROM emp
WHERE sal=(
SELECT MIN(sal) FROM emp;);
子查詢可以返回的結果數據類型一共分為四種:
單行單列 |
返回的是一個具體列的內容,可以理解為一個單值數據; |
單行多列 |
返回一行數據中多個列的內容; |
多行單列 |
返回多行記錄之中同一列的內容,相當於給出了一個操作范圍; |
多行多列 |
查詢返回的結果是一張臨時表; |
子查詢語法:
SELECT [DISTINCT] * | 分組字段1 [AS] [列別名] , [分組字段2 [AS] [列別名] , …],( SELECT [DISTINCT] * | 分組字段1 [AS] [列別名] , [分組字段2 [AS] [列別名] , …] FROM 表名稱1 [表別名1] , 表名稱2 [表別名2] …. [WHERE 條件(s)] [GROUP BY 分組字段1 , 分組字段2 , ….] [HAVING 過濾條件(s)] [ORDER BY 排序字段 ASC|DESC]) ... FROM 表名稱1 [表別名1] , 表名稱2 [表別名2] …, ( SELECT [DISTINCT] * | 分組字段1 [AS] [列別名] , [分組字段2 [AS] [列別名] , …] FROM 表名稱1 [表別名1] , 表名稱2 [表別名2] …. [WHERE 條件(s)] [GROUP BY 分組字段1 , 分組字段2 , ….] [HAVING 過濾條件(s)] [ORDER BY 排序字段 ASC|DESC]) [WHERE 條件(s) ... ( SELECT [DISTINCT] * | 分組字段1 [AS] [列別名] , [分組字段2 [AS] [列別名] , …] FROM 表名稱1 [表別名1] , 表名稱2 [表別名2] …. [WHERE 條件(s)] [GROUP BY 分組字段1 , 分組字段2 , ….] [HAVING 過濾條件(s)] [ORDER BY 排序字段 ASC|DESC])] [GROUP BY 分組字段1 , 分組字段2 , ….] [HAVING 過濾條件(s)...( SELECT [DISTINCT] * | 分組字段1 [AS] [列別名] , [分組字段2 [AS] [列別名] , …] FROM 表名稱1 [表別名1] , 表名稱2 [表別名2] …. [WHERE 條件(s)] [GROUP BY 分組字段1 , 分組字段2 , ….] [HAVING 過濾條件(s)] [ORDER BY 排序字段 ASC|DESC])] [ORDER BY 排序字段 ASC|DESC] ; |
子查詢常見操作:
WHERE子句 |
此時子查詢返回的結果一般都是 單行單列、單行多列、多行單列; |
HAVING子句 |
此時子查詢返回的都是 單行單列數據,同時為了使用統計函數操作; |
FROM子句 |
此時子查詢返回的結果一般都是 多行多列,可以按照一張數據表(臨時表)的形式操作。 |
二、WHERE子句中使用子查詢
- 可以在WHERE子句之中處理單行單列子查詢、多行單列子查詢、單行多列子查詢。
- WHERE子句可以判斷單個數值、多個數值;
- 使用IN、ANY、ALL可以處理多行單列子查詢;
- 利用EXISTS()可以判斷查詢結果是否為null。
1、單行單列(子句中只有一行,一列)
例子1:查詢出基本工資比ALLEN低的全部雇員信息 |
SELECT * FROM emp WHERE sal< ( SELECT sal FROM emp WHERE ename='ALLEN') ; |
例子2:查詢基本工資高於公司平均薪金的全部雇員信息 |
SELECT * FROM emp WHERE sal>( SELECT AVG(sal) FROM emp) ; |
例子3:查找出與ALLEN從事同一工作,並且基本工資高於雇員編號為7521的全部雇員信息 |
SELECT * FROM emp WHERE job=(SELECT job FROM emp WHERE ename='ALLEN') AND sal>(SELECT sal FROM emp WHERE empno=7521) ; |
2、單行多列(子句中有一行SCOTT,多列sal和job)
例子1:查詢與SCOTT從事同一工作且工資相同的雇員信息 |
SELECT * FROM emp WHERE (job,sal)=( SELECT job,sal FROM emp WHERE ename='SCOTT') AND ename<>'SCOTT' ; |
例子2:查詢與雇員7566從事同一工作且領導相同的全部雇員信息 |
SELECT * FROM emp WHERE ( job,mgr)=( SELECT job,mgr FROM emp WHERE empno=7566) AND empno<>7566 ; |
例子3:查詢與ALLEN從事同一工作且在同一年雇佣的全部雇員信息(包括ALLEN) |
SELECT * FROM emp WHERE (job, TO_CHAR(hiredate, 'yyyy'))=( SELECT job, TO_CHAR(hiredate, 'yyyy') FROM emp WHERE ename='ALLEN') ; |
3、多行單列
在使用多行子查詢時,主要使用三種操作符:IN、ANY、ALL
IN和NOT IN
- 如果在IN中子查詢返回的數值有null,那么不會有影響,如果NOT IN中子查詢返回數據有null,那么就表示不會有任何數據返回
例子1:查詢出與每個部門中最低工資相同的全部雇員信息 |
SELECT * FROM emp WHERE sal IN ( SELECT MIN(sal) FROM emp GROUP BY deptno) ; |
例子2:查詢出不與每個部門中最低工資相同的全部雇員信息 |
SELECT * FROM emp WHERE sal NOT IN ( SELECT MIN(sal) FROM emp GROUP BY deptno) ; |
ANY(SOME)
=ANY 表示與子查詢中的每個元素進行比較,功能與IN類似(然而<>ANY不等價於NOT IN); |
SELECT * FROM emp WHERE sal=ANY ( SELECT MIN(sal) FROM emp WHERE job='MANAGER' GROUP BY deptno) ; |
>ANY 比子查詢中返回結果的最小的要大(還包含了>=ANY); |
SELECT * FROM emp WHERE sal >ANY ( SELECT MIN(sal) FROM emp WHERE job='MANAGER' GROUP BY deptno) ; |
<ANY 比子查詢中返回結果的最大的要小(還包含了<=ANY) |
SELECT * FROM emp WHERE sal <ANY ( SELECT MIN(sal) FROM emp WHERE job='MANAGER' GROUP BY deptno) ; |
ALL操作符
<>ALL 等價於NOT IN(但是=ALL並不等價於IN); |
使用<>ALL操作符完成查詢 SELECT * FROM emp WHERE sal <>ALL ( SELECT MIN(sal) FROM emp WHERE job='MANAGER' GROUP BY deptno) ; |
>ALL 比子查詢中最大的值還要大(還包含了>=ALL); |
使用>ALL操作符完成查詢 SELECT * FROM emp WHERE sal >ALL ( SELECT MIN(sal) FROM emp WHERE job='MANAGER' GROUP BY deptno) ; |
<ALL 比子查詢中最小的值還要小(還包含了<=ALL)。 |
使用<ALL操作符完成查詢 SELECT * FROM emp WHERE sal <ALL ( SELECT MIN(sal) FROM emp WHERE job='MANAGER' GROUP BY deptno) ; |
空值數據判斷
在SQL之中提供了一個exists結構用於判斷子查詢是否有數據返回。如果子查詢中有數據返回,則exists結構返回true,反之返回false。
驗證exists結構 |
SELECT * FROM emp WHERE EXISTS( SELECT * FROM emp WHERE empno=9999) ; |
驗證exists結構 |
SELECT * FROM emp WHERE EXISTS( SELECT * FROM emp) ; |
使用NOT EXISTS |
SELECT * FROM emp WHERE NOT EXISTS( (SELECT * FROM emp WHERE empno=9999)); |
三、在HAVING子句中使用子查詢
在HAVING子句中使用子查詢,子查詢返回的都是單行單列數據,同時也可以在HAVING中利用統計函數進行判斷。
例子1:查詢部門編號、雇員人數、平均工資,並且要求這些部門的平均工資高於公司平均薪金 |
SELECT deptno, COUNT(empno), AVG(sal) FROM emp GROUP BY deptno HAVING AVG(sal)>( SELECT AVG(sal) FROM emp); |
例子2:查詢出每個部門平均工資最高的部門名稱及平均工資 |
SELECT d.dname, ROUND(AVG(e.sal),2) FROM emp e ,dept d WHERE e.deptno=d.deptno GROUP BY d.dname HAVING AVG(sal)=( SELECT MAX(AVG(sal)) FROM emp GROUP BY deptno) ; |
四、在FROM子句中使用子查詢
- 如果現在子查詢返回的數據是多行多列的,那么就可以將其當做一張數據表(同時存在多行多列)來使用,並且這種子查詢一般都出現在FROM子句之中。
- FROM子句出現的子查詢返回結構為多行多列;
- 利用子查詢可以解決多表查詢所帶來的性能問題。
例子1:要求查詢出每個部門的編號、名稱、位置、部門人數、平均工資 使用子查詢能解決多表查詢所帶來的性能問題 |
多字段分組實現方式(會產生笛卡爾積): 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 子查詢實現方式: SELECT d.deptno,d.dname,d.loc,temp.count,temp.avg FROM dept d, (SELECT deptno dno, COUNT(empno) count , ROUND(AVG(sal),2) avg FROM emp GROUP BY deptno) temp WHERE d.deptno=temp.dno(+) ; |
例子2:查詢出所有在部門“SALES”(銷售部)工作的員工的編號、姓名、基本工資、獎金、職位、雇佣日期、部門的最高和最低工資。 |
確定所需要的數據表: dept表:銷售部 emp表:員工信息 emp表:統計最高最低工資 沒有關聯字段,也不需要關聯字段 |
步驟一:查詢銷售部的部門編號 SELECT deptno FROM dept WHERE dname='SALES'; 步驟二:此部門的雇員信息 SELECT empno,ename,sal,comm,job,hiredate FROM emp WHERE deptno=( SELECT deptno FROM dept WHERE dname='SALES' ); 步驟三:最高和最低工資,使用MAX()和MIN()函數 因為統計函數有限制,要么單獨使用,要么結合GROUP BY函數,統計函數嵌套時不允許出現任何字段。 目前在整個SELECT查詢里面需要統計查詢,但無法直接使用統計函數,可以在子查詢中完成,而且這個子查詢一定返回一個多行多列,在FROM子句中出現 SELECT e.empno, e.ename, e.sal, e.comm, e.job, e.hiredate, temp.max, temp.min FROM emp e, ( SELECT deptno dno,MAX(sal) max,MIN(sal) min FROM emp GROUP BY deptno ) temp è 子查詢負責統計信息,使用temp表示臨時表的統計結果 WHERE deptno=( SELECT deptno FROM dept WHERE dname='SALES' AND e.deptno=temp.dno ); |
例子3:查詢出所有薪金高於公司平均薪金的員工編號、姓名、基本工資、職位、雇佣日期,所在部門名稱、位置,上級領導姓名,公司的工資等級,部門人數、平均工資、平均服務年限。 |
分析: 確定所需要的數據表 emp表:員工編號,姓名,工資,職位,雇佣日期 dept表:部門信息 emp表:自身關聯上級領導姓名 salgrade表:工資等級 emp表:統計人數,平均工資,平均服務年限 確定已知的關聯字段 雇員和部門:emp.deptno=dept.deptno 雇員和領導:emp.mgr=memp.empno 雇員和工資等級:emp.sal BETWEEN salgrade.losal AND salgrade.hisal |
步驟一:AVG()函數統計公司平均薪金 SELECT avg(sal) FROM emp; 結果是單行單列,只能在WHERE和HAVING子句中出現,這里沒有分組,只能在WHERE中出現 步驟二:查詢高於平均薪金的員工編號,姓名,基本工資等 SELECT e.empno, e.ename, e.sal, e.job, e.hiredate FROM emp e WHERE e.sal >(SELECT avg(sal) FROM emp); 步驟三:和dept表、salgrade表和emp表領導信息進行關聯(多表關聯使用關聯字段消除笛卡爾積,由於有的員工沒有領導,考慮外連接) SELECT e.empno, e.ename, e.sal, e.job, e.hiredate ,d.dname, d.loc, m.ename mname, s.grade FROM emp e, dept d, emp m, salgrade s WHERE e.sal >(SELECT avg(sal) FROM emp) AND e.deptno=d.deptno AND e.mgr=m.empno(+) AND e.sal BETWEEN s.losal AND s.hisal; 步驟四:部門人數,平均工資、平均服務年限,需要使用函數AVG(),這里使用子查詢 SELECT e.empno, e.ename, e.sal, e.job, e.hiredate ,d.dname, d.loc, m.ename mname, s.grade ,temp.count,temp.avgsal, temp.avgyear FROM emp e, dept d, emp m, salgrade s,( SELECT deptno dno ,COUNT(empno) count,ROUND(AVG(sal),2) avgsal,ROUND(AVG(MONTHS_BETWEEN(SYSDATE,hiredate)/12),2) avgyear FROM emp GROUP BY deptno ) temp WHERE e.sal > (SELECT avg(sal) FROM emp) AND e.deptno=d.deptno AND e.mgr=m.empno(+) AND e.sal BETWEEN s.losal AND s.hisal AND e.deptno=temp.dno; |
例子4:列出薪金比“ALLEN”或“CLARK”多的所有員工的編號、姓名、基本工資、部門名稱、其領導姓名,部門人數。 |
分析: 確定所需要的數據表 emp表:員工編號,姓名,工資,職位,雇佣日期 dept表:部門信息 emp表:自身關聯上級領導姓名 emp表:統計部門人數 確定已知的關聯字段 雇員和部門:emp.deptno=dept.deptno 雇員和領導:emp.mgr=memp.empno |
步驟一:查詢"ALLEN”或“CLARK”的薪金 SELECT e.ename,e.sal FROM emp e WHERE e.ename IN('JONES','CLARK'); (在WHERE子句中這里返回的是多行單列,這里使用IN\ANY\ALL) 步驟二:查詢薪金比“ALLEN”或“CLARK”多的所有員工,加上dept表查詢部門以及emp表查詢領導信息 SELECT e.ename,e.sal,e.empno,d.dname, m.ename mname FROM emp e ,dept d, emp m WHERE e.sal > ANY( SELECT sal FROM emp WHERE ename IN('JONES','CLARK') ) AND e.deptno=d.deptno AND e.mgr=m.empno(+); 步驟三:查詢部門人數,需要使用COUNT()函數 SELECT deptno dno,COUNT(empno) count FROM emp GROUP BY deptno 這里要注意,統計函數要么單獨使用,要和分組字段使用就必須使用GROUP BY,否則無法使用的. SELECT e.ename,e.sal,e.empno,d.dname, m.ename mname,temp.count FROM emp e ,dept d, emp m,(SELECT deptno dno,COUNT(empno) count FROM emp GROUP BY deptno) temp WHERE e.sal > ANY( SELECT sal FROM emp WHERE ename IN('JONES','CLARK') ) AND e.deptno=d.deptno AND e.mgr=m.empno(+) AND e.deptno=temp.dno; |
例子5:列出公司各個部門的經理(假設每個部門只有一個經理,job為“MANAGER”)的姓名、薪金、部門名稱、部門人數、部門平均工資。 |
分析: 確定所需要的數據表 emp表:員工編號,姓名,工資,經理 dept表:部門信息 emp表:統計部門人數,平均工資 確定已知的關聯字段 雇員(經理)和部門:emp.deptno=dept.deptno |
步驟一:列出經理的姓名,薪資,部門名稱 SELECT e.ename, e.sal, d.dname FROM emp e, dept d WHERE e.job='MANAGER' AND e.deptno=d.deptno; 步驟二:統計部門人數,平均工資 SELECT e.ename, e.sal, d.dname,temp.count, temp.avg FROM emp e, dept d,( SELECT deptno dno,COUNT(empno) count,ROUND(AVG(sal),2) avg FROM emp GROUP BY deptno ) temp WHERE e.job='MANAGER' AND e.deptno=d.deptno AND e.deptno=temp.dno; |
五、在SELECT子句中使用子查詢
例子1:查詢出公司每個部門的編號、名稱、位置、部門人數、平均工資 |
分析: 確定所需要的表: dept表:部門信息 emp表:平均工資,部門人數 |
步驟一:查詢部門信息 SELECT deptno,dname,loc FROM dept d; 步驟二:查詢部門人數 SELECT COUNT(empno) FROM emp WHERE deptno=dept.deptno GROUP BY deptno 步驟三:查詢平均工資 SELECT AVG(sal) FROM emp WHERE deptno=dept.deptno GROUP BY deptno 步驟四:將平均工資和部門人數放在SELECT子句的字段中 SELECT deptno,dname,loc, ( SELECT COUNT(empno) FROM emp WHERE deptno=d.deptno GROUP BY deptno) count, (SELECT AVG(sal) FROM emp WHERE deptno=d.deptno GROUP BY deptno) sal FROM dept d; |
六、WITH子句
- 臨時表是一個查詢結果,查詢結果返回的是多上多列,那么可以將其定義在FROM子句之中,表示其為一張臨時表.
- 除了在FROM子句之中出現臨時表之外,也可以利用WITH子句直接定義臨時表.
- 可以使用WITH子句創建臨時查詢表
- WITH子句可以構建一張臨時表供查詢使用。
- WITH子句提供了一種定義臨時表的操作方法,如果在一個查詢之中,要反復使用到一些數據,那么就可以將這些數據定義在WITH子句之中。
使用WITH子句將emp表中的數據定義為臨時表 |
WITH e AS ( SELECT * FROM emp) SELECT * FROM e ;
|
查詢每個部門的編號、名稱、位置、部門平均工資、人數 |
WITH e AS ( SELECT deptno dno , ROUND(AVG(sal),2) avg , COUNT(sal) count FROM emp GROUP BY deptno)
SELECT d.deptno,d.dname,d.loc,e.count,e.avg FROM e , dept d WHERE e.dno(+)=d.deptno ; |
查詢每個部門工資最高的雇員編號、姓名、職位、雇佣日期、工資、部門編號、部門名稱,顯示的結果按照部門編號進行排序 |
WITH e AS ( SELECT deptno dno , MAX(sal) max FROM emp GROUP BY deptno)
SELECT em.empno , em.ename , em.job , em.hiredate , em.sal , d.deptno,d.dname FROM e , emp em , dept d WHERE e.dno=em.deptno AND em.sal=e.max AND e.dno=d.deptno ORDER BY em.deptno ; |
FROM子句中的例子:例子5:列出公司各個部門的經理(假設每個部門只有一個經理,job為“MANAGER”)的姓名、薪金、部門名稱、部門人數、部門平均工資。 |
WITH t AS( SELECT deptno dno,COUNT(empno) count,ROUND(AVG(sal),2) avg FROM emp GROUP BY deptno) SELECT e.ename, e.sal, d.dname,t.count, t.avg FROM emp e, dept d,t WHERE e.job='MANAGER' AND e.deptno=d.deptno AND e.deptno=t.dno; |