oracle之復雜查詢(下):子查詢


復雜查詢(下):子查詢

8. 1 非關聯子查詢:返回的值可以被外部查詢使用。子查詢可以獨立執行的(且僅執行一次)。

8.1.1 單行單列子查詢,子查詢僅返回一個值,也稱為標量子查詢,采用單行比較運算符(>,<,=,<>,>=,<=)

例:內部SELECT子句只返回一行結果

SQL>select ename,sal
from emp
where sal > (
     select sal from emp
     where ename='JONES')
/
例:和員工7369從事相同工作並且工資大於員工7876的員工的姓名和工作

SQL>select ename,job,sal
from emp
where job=(
        select job
        from emp
        where empno=7369
        )
and
       sal > (
        select sal
        from emp
        where empno=7876
        )
/

8.1.2 多行單列子查詢,采用多行比較運算符(all, any, in,not in)

all  (>大於最大的,<小於最小的)

SQL> select ename,sal from emp where sal >all (2000,3000,4000);

ENAME             SAL
---------- ----------
KING             5000


例:查找高於所有部門的平均工資的員工(>比子查詢中返回的列表中最大的大才行)

SQL> select ename, job, sal from emp where sal > all(select avg(sal) from emp group by deptno);


ENAME      JOB              SAL
---------- --------- ----------
JONES      MANAGER        2975
SCOTT      ANALYST         3000
KING       PRESIDENT       5000
FORD       ANALYST         3000

SQL> select avg(sal) from emp group by deptno;   //子查詢結果

  AVG(SAL)
----------
1566.66667
      2175
2916.66667

8.1.3 在多行子查詢中使用any (>大於最小的,<小於最大的)

>any的意思是:比子查詢中返回的列表中最小的大就行, 注意和all的區別,all的條件苛刻,any的條件松闊,
any強調的是只要有任意一個符合就行了,所以>any只要比最小的那個大就行了,沒必要比最大的還大。

select ename, sal from emp where sal >any (2000,3000,4000);

ENAME             SAL
---------- ----------
JONES            2975
BLAKE            2850
CLARK            2450
SCOTT            3000
KING              5000
FORD             3000

8.1.4 在多行子查詢中使用in  (逐個比較是否有匹配值)

SQL> select ename, sal from emp where sal in (800,3000,4000);

ENAME             SAL
---------- ----------
SMITH             800
SCOTT            3000
FORD             3000

NOT運算操作符可以使用在IN操作上,但不能使用在ANY,ALL操作。

SQL> select ename, sal from emp where sal not in (800,3000,4000);

ENAME             SAL
---------- ----------
ALLEN            1600
WARD             1250
JONES            2975
MARTIN          1250
BLAKE             2850
CLARK            2450
KING              5000
TURNER          1500
ADAMS           1100
JAMES             950
MILLER           1300

已選擇11行。

8.1.5 多行多列子查詢,子查詢返回多列結果集,有成對比較、非成對比較兩種形式。

測試准備

SQL>create table emp1 as select * from emp;
SQL>update emp1 set sal=1600,comm=300 where ename='SMITH';        //SMITH是20部門的員工
SQL>update emp1 set sal=1500,comm=300 where ename='CLARK';          //CLARK是10部門的員工
SQL> select * from emp1;

     EMPNO ENAME      JOB              MGR HIREDATE                   SAL       COMM     DEPTNO
---------- ---------- --------- ---------- ------------------- ---------- ---------- ----------
      7369 SMITH      CLERK                7902 1980-12-17 00:00:00       1600        300         20
      7499 ALLEN      SALESMAN        7698 1981-02-20 00:00:00       1600        300         30
      7521 WARD       SALESMAN       7698 1981-02-22 00:00:00       1250        500         30
      7566 JONES      MANAGER         7839 1981-04-02 00:00:00       2975                       20
      7654 MARTIN     SALESMAN      7698 1981-09-28 00:00:00       1250       1400        30
      7698 BLAKE      MANAGER         7839 1981-05-01 00:00:00       2850                        30
      7782 CLARK      MANAGER         7839 1981-06-09 00:00:00       1500        300         10
      7788 SCOTT      ANALYST           7566 1987-04-19 00:00:00       3000                       20
      7839 KING       PRESIDENT                   1981-11-17 00:00:00       5000                      10
      7844 TURNER     SALESMAN      7698 1981-09-08 00:00:00       1500          0           30
      7876 ADAMS      CLERK              7788 1987-05-23 00:00:00       1100                       20
      7900 JAMES      CLERK                7698 1981-12-03 00:00:00        950                       30
      7902 FORD       ANALYST           7566 1981-12-03 00:00:00       3000                       20
      7934 MILLER     CLERK                7782 1982-01-23 00:00:00       1300                      10

已選擇14行。

查詢條件:查找emp1表中是否有與30部門的員工工資和獎金相同的其他部門的員工。
(注意看一下:現在20部門的SIMTH符合這個條件,它與30部門的ALLEN 有相同的工資和獎金)


成對多列子查詢:

特點是主查詢每一行中的列都要與子查詢返回列表中的相應列同時進行比較,只有各列完全匹配時才顯示主查詢中的該數據行。

分解一下:

第一步,我們可以先找出emp1表中30號部門的工資和獎金的結果集,(此例沒有對comm的空值進行處理)

SQL> select sal,comm from emp1 where deptno=30;

       SAL       COMM
---------- ----------
      1600        300
      1250        500
      1250       1400
      2850
      1500          0
       950

已選擇6行。

第二步,列出emp1表中屬於這個結果集的所有員工。

SQL> select * from emp1 where (sal,comm) in (select sal,comm from emp1 where deptno=30);

     EMPNO ENAME      JOB              MGR HIREDATE                   SAL       COMM     DEPTNO
---------- ---------- --------- ---------- ------------------- ---------- ---------- ----------
      7499 ALLEN      SALESMAN        7698 1981-02-20 00:00:00       1600        300         30
      7369 SMITH      CLERK                7902 1980-12-17 00:00:00       1600        300         20
      7521 WARD       SALESMAN        7698 1981-02-22 00:00:00       1250        500         30
      7654 MARTIN     SALESMAN        7698 1981-09-28 00:00:00       1250       1400       30
      7844 TURNER     SALESMAN        7698 1981-09-08 00:00:00       1500          0         30

可以這樣想:相當於謂詞in(...)中有上面這6行內容,所以上面句子相當於:
select * from emp1 where (sal,comm) in(
(1600,300),(1250,500),(1250,1400),(2850,null),(1500,0),(950,null));

第三步, 再去掉30號部門后,就顯示出了在emp1表中與30部門中任意一個員工的工資和獎金完全相同的,但不是30部門的那些員工的信息。

SQL>
select ename,deptno,sal,comm from emp1
  where (sal,comm) in (select sal,comm from emp1 where deptno=30)
and deptno<>30
/


ENAME          DEPTNO        SAL       COMM
---------- ---------- ---------- ----------
SMITH              20            1600        300


考點:1)成對比較是不能使用>any或>all等多行單列比較符的。2)成對比較時的多列順序和類型必須一一對應。

8.1.6 與非成對比較(含布爾運算)的區別

例:非成對比較

SQL>select ename,deptno,sal,comm
from emp1
where sal in(
        select sal
        from emp1
        where deptno=30)
and
        nvl(comm,0) in (
        select nvl(comm,0)
        from emp1
        where deptno=30)
and deptno<>30
/


ENAME          DEPTNO        SAL       COMM
---------- ---------- ---------- ----------
SMITH              20       1600        300
CLARK              10       1500        300

兩個子查詢返回的值分別與主查詢中的sal和comm列比較,
如果員工的工資與30部門任意一個員工相同,同時,獎金也與30部門的其他員工相同,那么得到了兩個員工的信息。
可見,成對比較(使用where (列,列))比非成對比較(使用where 列 and 列) 更為嚴苛。


8.1.7 關於布爾運算符not

 not 就是否定后面的比較符,基本的形式如下

where empno=7788            where NOT (empno=7788)
where ename LIKE 'S%'             where ename NOT LIKE 'S%'
where deptno IN (20,30)            where deptno NOT IN (20,30)
where sal BETWEEN 1500 AND 3000          where sal NOT BETWEEN 1500 AND 3000
where comm IS NULL            where comm IS NOT NULL
where EXISTS (select子查詢)            where NOT EXISTS (select子查詢)

8.1.8 not in 在子查詢中的空值問題:

"in"與"not in"遇到空值時情況不同,對於"not in" 如果子查詢的結果集中有空值,那么主查詢得到的結果集也是空。

查找出沒有下屬的員工,即普通員工,(該員工號不在mgr之列的)

SQL>select ename from emp where empno not in (select mgr from emp);

no rows selected        

上面的結果不出所料,主查詢沒有返回記錄。這個原因是在子查詢中有一個空值,而對於not in這種形式,一旦子查詢出現了空值,則主查詢記錄結果也就返回空了。

注意:not后不能跟單行比較符,只有not in組合,沒有not any 和not all的組合,但not后可以接表達式 如:

where empno not in(...)與where not empno in(...)兩個寫法都是同樣結果,前者是not in組合,后者是not一個表達式。

例:排除空值的影響

SQL>select ename from emp where empno not in (select nvl(mgr,0)from emp);


8.1.9 from子句中使用子查詢(也叫內聯視圖)

例:員工的工資大於他所在的部門的平均工資的話,顯示其信息。

分兩步來考慮:

第一步,先看看每個部門的平均工資,再把這個結果集作為一個內聯視圖。

SQL> select deptno,avg(sal) salavg from emp group by deptno;

    DEPTNO     SALAVG
---------- ----------
        30 1566.66667
        20       2175
        10 2916.66667


第二步,把這個內聯視圖起一個別名b, 然后和emp 別名e 做連接,滿足條件即可。
SQL>
select e.ename, e.sal, e.deptno, b.salavg
from emp e, (select deptno,avg(sal) salavg from emp group by deptno) b
where e.deptno=b.deptno and e.sal > b.salavg
/

ENAME             SAL     DEPTNO     SALAVG
---------- ---------- ---------- ----------
ALLEN            1600         30     1566.66667
JONES            2975         20       2175
BLAKE            2850         30     1566.66667
SCOTT            3000         20       2175
KING             5000         10     2916.66667
FORD             3000         20       2175


8.2關聯子查詢

其子查詢(內部,inner)會引用主查詢(外部,outer)查詢中的一列或多列。在執行時,外部查詢的每一行都被一次一行地傳遞給子查詢,子查詢依次讀取外部查詢傳遞來的每一值,並將其用到子查詢上,直到外部查詢所有的行都處理完為止,最后返回查詢結果。

理論上主查詢有n行,子查詢被調用n次。

例1,關聯查詢用於select語句

8.1.9小節的例子,顯示員工的工資大於他所在部門的平均工資,也可以使用關聯查詢。

SQL> select ename,sal,deptno from emp outer where sal> (select avg(sal) from emp inner where inner.deptno=outer.deptno);

ENAME             SAL     DEPTNO
---------- ---------- ----------
ALLEN            1600         30
JONES            2975         20
BLAKE            2850         30
SCOTT            3000         20
KING             5000         10
FORD             3000         20

例2,關聯查詢用於update語句

SQL> create table emp1 as (select e.empno,e.ename,d.loc,d.deptno from emp e,dept d where e.deptno=d.deptno(+));
SQL> update emp1 set loc=null;
SQL> commit;

如何通過關聯查詢再將emp1表更新回原值。

SQL> update emp1 e set loc=(select d.loc from dept d where e.deptno=d.deptno);

例3. 關聯查詢中的特殊形式,使用EXISTS或NOT EXISTS

EXISTS關心的是在子查詢里能否找到一個行值(哪怕有10行匹配,只要找到一行就行),如果子查詢有行值,則立即停止子查詢的搜索,然后返回邏輯標識TRUE, 如果子查詢沒有返回行值,則返回邏輯標識FALSE, 子查詢要么返回T,要么返回F,以此決定了主查詢的調用行的去留,然后主查詢指針指向下一行,繼續調用子查詢...

EXISTS的例子:顯示出emp表中那些員工不是普通員工(屬於大小領導的)。

SQL> select empno,ename,job,deptno from emp outer where exists (select 'X' from emp where mgr=outer.empno);

     EMPNO ENAME      JOB           DEPTNO
---------- ---------- --------- ----------
      7566 JONES      MANAGER           20
      7698 BLAKE      MANAGER           30
      7782 CLARK      MANAGER           10
      7788 SCOTT      ANALYST             20
      7839 KING       PRESIDENT           10
      7902 FORD       ANALYST             20

已選擇6行。

說明:exists子查詢中select 后的‘X'只是一個占位,它返回什么值無關緊要,它關心的是子查詢中否‘存在’,即子查詢的where條件能否有‘結果’,一旦子查詢查到一條記錄滿足where條件,則立即返回邏輯‘TRUE’,(就不往下查了)。否則返回‘FALSE’。

NOT EXISTS的例子:顯示dept表中還沒有員工的部門。

SQL> select deptno,dname from dept d where not exists (select 'X' from emp where deptno=d.deptno);

    DEPTNO DNAME
---------- --------------
        40 OPERATIONS


對於關聯子查詢,在某種特定的條件下,比如子查詢是個大表,且連接字段建立了索引,那么使用exists比in的效率可能更高。

8.10 關於別名的使用

有表別名和列別名, 表別名用於多表連接或子查詢中,列別名用於列的命名規范。

如果別名的字面值有特殊字符,需要使用雙引號。如:"AB C"

8.3 必須使用別名的地方:

1)兩表連接后,select 投影中有相同命名的列,必須使用表別名區別標識(自然連接中的公共列則使用相反原則)

select ename,d.deptno from emp e,dept d where e.deptno=d.deptno;

2)使用create * {table |view} as select ...語句創建一個新的對象,其字段名要符合對象中字段的規范,不能是表達式或函數等非規范字符,而使用別名可以解決這個問題。

create table emp1 as select deptno,avg(sal) salavg from emp group by deptno;
create view v as select deptno,avg(sal) salavg from emp group by deptno;

3)使用內聯視圖時, 若where子句還要引用其select中函數的投影, 使用別名可以派上用場。

select * from (select avg(sal) salavg from emp) where salavg>2000;

4)當以內聯視圖作為多表連接,主查詢投影列在形式上不允許單行字段(或函數)與聚合函數並列,解決這個問題是使在內聯視圖中為聚合函數加別名,然后主查詢的投影中引用其別名。

select e.ename,e.sal,b.deptno,b.salavg
       from emp e,(select deptno,avg(sal) salavg from emp group by deptno) b
       where e.deptno=b.deptno;

5)rownum列是Oracle的偽列,加別名可以使它成為一個表列,這樣才可以符合SQL99標准中的連接和選擇。

SQL> select * from (select ename,rownum rn from emp) where rn>5;

6)不能使用別名的地方:

在一個獨立的select結構的投影中使用了列別名,不能在其后的where 或having中直接引用該列別名(想想為什么?)。

select ename,sal salary from emp where salary>2000;                --錯

select deptno,avg(sal) salavg from emp group by deptno having salavg>2000; --錯


8.4 簡單查詢與復雜查詢練習題:

1)列出emp表工資最高的前三名員工信息

select * from (select * from emp order by sal desc) where rownum < 4;  

關於rownum 偽列使用特別需要注意兩點:

1,rownum>時不會返回任何行
2,rownum< 和and並用時,是在另一個條件基礎上的rownum<  ,而不是兩個獨立條件的並集(intersect)

體會一下:
SQL> select ename,sal,deptno from emp where deptno=10;

ENAME             SAL     DEPTNO
---------- ---------- ----------
CLARK            2450         10
KING              5000         10
MILLER           1300         10

SQL> select ename,sal,deptno from emp where rownum <=1;

ENAME             SAL     DEPTNO
---------- ---------- ----------
SMITH             800         20

SQL> select ename,sal,deptno from emp where rownum <=1 and deptno=10;

ENAME             SAL     DEPTNO
---------- ---------- ----------
CLARK            2450         10

//即在deptno=10的基礎上再rownum<=1

2) 列出emp表第5-10名員工(按sal大--小排序)的信息(結果集的分頁查詢技術)

SQL> select * from (select t1.*, rownum rn from (select * from emp order by sal desc) t1) where rn between 5 and 10;
 
EMPNO ENAME      JOB         MGR HIREDATE          SAL      COMM DEPTNO         RN
----- ---------- --------- ----- ----------- --------- --------- ------ ----------
 7698 BLAKE      MANAGER    7839 1981-5-1      2850.00                   30              5
 7782 CLARK      MANAGER    7839 1981-6-9      2450.00                   10              6
 7499 ALLEN      SALESMAN   7698 1981-2-20     1600.00    3 00.00     30              7
 7844 TURNER   SALESMAN   7698 1981-9-8      1500.00          0.00     30              8
 7934 MILLER     CLERK          7782 1982-1-23     1300.00                   10               9
 7521 WARD      SALESMAN   7698 1981-2-22     1250.00    500.00     30             10
 
6 rows selected

3)從列出emp表中顯示員工和經理對應關系表。(emp自連,利用笛卡爾積)

select a.empno,a.ename,a.mgr,b.empno,b.ename,b.mgr
from emp a, emp b
where a.mgr=b.empno;


4)要求列出emp表中最高工資的員工所在工作地點。(emp+dept左外,如果該員工不屬於任何部門則列出員工姓名)

select a.ename, d.loc from
(select * from emp where sal=
(select max(sal) from emp)) a left join dept d on a.deptno=d.deptno ;


5)CTAS方法建立dept1,將dept1表增加一列person_count,要求根據emp表填寫dept1表的各部門員工合計數(典型的關聯查詢)。

SQL> create table dept1 as select * from dept;
SQL> alter table dept1 add person_count int;
SQL> update dept1 d set person_count=(select count(*) from emp e where e.deptno=d.deptno);
SQL> select * from dept1;

    DEPTNO DNAME          LOC           PERSON_COUNT
---------- -------------- ------------- ------------
        10 ACCOUNTING     NEW YORK                3
        20 RESEARCH           DALLAS                     5
        30 SALES                  CHICAGO                  6
        40 OPERATIONS      BOSTON               0


6) 復雜select查詢,以HR用戶的幾個表為例(PPT-II-8),顯示歐洲地區員工的平均工資及人數(使用多表連接及內聯視圖)
SQL>
select avg(salary),count(salary) from
  (select e.first_name,e.salary,d.department_id,l.location_id, c.country_id, r.region_id,r.region_name
          from employees e,departments d,locations l,countries c,regions r
            where e.department_id=d.department_id and d.location_id=l.location_id
              and l.country_id=c.country_id and c.region_id=r.region_id and r.region_name='Europe');

AVG(SALARY) COUNT(SALARY)
----------- -------------
 8916.66667            36


7)同上題(使用嵌套子查詢技術)

SQL>
select avg(salary),count(*) from employees where department_id in
(select department_id from departments where location_id in
 (select location_id from locations where country_id in
   (select country_id from countries where region_id=
    (select region_id from regions where region_name='Europe')
   )
 )
);

AVG(SALARY) COUNT(SALARY)
----------- -------------
 8916.66667            36

分解如下:

SQL> select region_id from regions where region_name='Europe';

 REGION_ID
----------
         1

SQL> select country_id from countries where region_id=1;

CO
--
BE
CH
DE
DK
FR
IT
NL
UK

SQL> select location_id from locations where country_id in ('BE','CH','DE','DK','FR','IT','NL','UK');

LOCATION_ID
-----------
       1000
       1100
       2400
       2500
       2600
       2700
       2900
       3000
       3100


SQL> select department_id from departments where location_id in (1000,1100,2400,2500,2600,2700,2900,3000,3100);

DEPARTMENT_ID
-------------
           40
           70
           80

SQL> select avg(salary),count(*) from employees where department_id in(40,70,80);

AVG(SALARY)   COUNT(*)
----------- ----------
 8916.66667         36



免責聲明!

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



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