SQL Fundamentals: 子查詢 || WHERE,HAVING,FROM,SELECT子句中使用子查詢,WITH子句


 SQL Fundamentals || Oracle SQL語言

 

子查詢(基礎)

1、認識子查詢

2、WHERE子句中使用子查詢

3、在HAVING子句中使用子查詢

4、在FROM子句中使用子查詢

5、在SELECT子句中使用子查詢

6、WITH子句

 

子查詢(進階)

7、分析函數

8、行列轉換

9、設置數據層次

 

 


 

 

一、認識子查詢

 

  • 子查詢就是指的在一個完整的查詢語句之中,嵌套若干個不同功能的小查詢,從而一起完成復雜查詢(復雜查詢=限定查詢+多表查詢+統計查詢+子查詢)的一種編寫形式,為了讓讀者更加清楚子查詢的概念。
  • 一個查詢語句內部可以定義多個子查詢;
  • 子查詢一般在WHEREFROMHAVING子句之中出現較多,也可以在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子句可以判斷單個數值、多個數值;
  • 使用INANYALL可以處理多行單列子查詢;
  • 利用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,多列saljob

例子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、多行單列

在使用多行子查詢時,主要使用三種操作符:INANYALL

INNOT 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) ;

 

ANYSOME

=ANY

表示與子查詢中的每個元素進行比較,功能與IN類似(然而<>ANY不等價於NOT IN);

      • 使用=ANY操作符完成查詢

SELECT * FROM emp

WHERE sal=ANY (

      SELECT MIN(sal)

      FROM emp

      WHERE job='MANAGER'

      GROUP BY deptno) ;

>ANY

比子查詢中返回結果的最小的要大(還包含了>=ANY);

      • 使用>ANY操作符完成查詢

SELECT * FROM emp

WHERE sal >ANY (

      SELECT MIN(sal)

      FROM emp

      WHERE job='MANAGER'

      GROUP BY deptno) ;

<ANY

比子查詢中返回結果的最大的要小(還包含了<=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;


 

 


免責聲明!

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



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