日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Oracle查询优化-03操作多个表

發(fā)布時間:2025/3/21 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Oracle查询优化-03操作多个表 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

  • 1 記錄集的疊加
    • 問題
    • 解決方案
    • 結(jié)論
  • 2 組合相關(guān)的行
    • 問題
    • 解決方案
    • 結(jié)論
  • 4 INEXISTS 和 INNER JOIN
    • 問題
    • 解決方案
      • IN
      • EXISTS
      • INNER JOIN
    • 結(jié)論
  • 5 INNER JOINLEFT JOINRIGHT JOIN 和 FULL JOIN 解析
    • 問題
    • 解決方案
      • inner join的特點
      • left join的特點
      • right join的特點
      • full join的特點
  • 6 自關(guān)聯(lián)
    • 問題
    • 解決方案
    • 結(jié)論
  • 7 NOT INNOT EXISTS 和 LEFT JOIN
    • 問題
    • 解決方案
      • not in
      • not exists
      • left join
    • 結(jié)論
  • 8 外連接中的條件不要亂放
    • 問題
    • 解決方案
  • 9 檢測兩個表中的數(shù)據(jù)及對應數(shù)據(jù)的條數(shù)是否相同
    • 問題
    • 解決方案
  • 10 聚集與內(nèi)連接
    • 問題
    • 解決方案
    • 結(jié)論
  • 11 聚集與外連接
    • 問題
    • 解決方案
    • 結(jié)論
  • 12 從多個表中返回丟失的數(shù)據(jù)
    • 問題
    • 解決方案
      • full join
      • union all
  • 13 多表查詢時的空值處理
    • 問題
    • 解決方案

3.1 記錄集的疊加

問題

要將來自多個表的數(shù)據(jù)組織到一起,就像將一個結(jié)果集疊加到另外一個上面一樣。 這些表不必有相同的關(guān)鍵字,但是他們對應列的數(shù)據(jù)類型必須相同。

解決方案

使用union all 把多個表中的行組合到一起。

select ename, deptnofrom empwhere deptno = 10 union all select '-----', deptnofrom dept union all select dname, deptno from dept;

結(jié)論

  • UNION ALL將多個來源的行組合起來,放到一個結(jié)果集中。 所有select列表中的項目數(shù)和對應項目的數(shù)據(jù)類型必須要匹配。

  • UNION ALL會包括重復的項目,如果要篩選掉重復項,可以使用UNION運算符。

  • 如果使用UNION而不是UNION ALL,很可能是為了去除重復項而進行排序操作。 在處理大結(jié)果集時要記住,使用UNION子句大致相當于下面的查詢,對UNION ALL子句的查詢結(jié)果使用DISTINCT子句

  • SQL> select distinct deptno 2 from (select deptno from dept 3 union all 4 select deptno from dept);DEPTNO ------ 30 20 40 10SQL> SQL> select deptno 2 from dept 3 union 4 select deptno from dept 5 ;DEPTNO ------ 10 20 30 40SQL>
  • 通常,查詢中不要使用distinct,除非確定有必要這樣做; 對于UNION而言也是如初,除非確定有必要,一般使用UNION ALL,而不適用UNION。


  • 3.2 組合相關(guān)的行

    問題

    多表有一些相同的列,或者有些列的值相同,需要通過關(guān)聯(lián)這些列得到結(jié)果。

    解決方案

    select a.ename ,b.dname from emp a ,dept b where a.deptno = b.deptno and a.deptno = 10 ;select a.ename ,b.dname from emp a inner join dept b on a.deptno = b.deptno where a.deptno = 10 ;

    結(jié)論

    第二種解決方式是利用顯示的JOIN子句(inner 關(guān)鍵字可省略),如果希望將聯(lián)接邏輯關(guān)系放在from子句中,而不是在where 子句中,可以使用JOIN子句, 這兩種方式都符合ANSI標準。


    3.4 IN、EXISTS 和 INNER JOIN

    問題

    先創(chuàng)建一個表EMP2

    create table emp2 asselect ename, job, sal, comm from emp where job = 'CLERK';

    要求返回與emp2(ename, job, sal)中數(shù)據(jù)相匹配的emp(ename, job, sal,deptno)信息

    有in , exists 和 inner join 三種寫法,為了加強理解,我們來看下三種寫法及其對應的執(zhí)行計劃。

    解決方案

    ORACLE VERSION : Oracle Database 11g Enterprise Edition Release 11.2.0.4.0

    IN

    SQL> explain plan for 2 select ename, job, sal, deptno 3 from emp 4 where (ename, job, sal) in (select ename, job, sal from emp2);ExplainedSQL> select * from table(dbms_xplan.display());PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 4039873364 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 4 | 260 | 6 (0)| 00:00:01 | |* 1 | HASH JOIN SEMI | | 4 | 260 | 6 (0)| 00:00:01 | | 2 | TABLE ACCESS FULL| EMP | 14 | 546 | 3 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL| EMP2 | 4 | 104 | 3 (0)| 00:00:01 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("ENAME"="ENAME" AND "JOB"="JOB" AND "SAL"="SAL") Note ----- - dynamic sampling used for this statement (level=2)19 rows selectedSQL>

    EXISTS

    SQL> explain plan for 2 select ename, job, sal, deptno 3 from emp a where exists (select * from emp2 b 4 where b.ename= a.ename 5 and b.job = a.job 6 and b.sal = a.sal) ;ExplainedSQL> select * from table(dbms_xplan.display());PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 4039873364 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 4 | 260 | 6 (0)| 00:00:01 | |* 1 | HASH JOIN SEMI | | 4 | 260 | 6 (0)| 00:00:01 | | 2 | TABLE ACCESS FULL| EMP | 14 | 546 | 3 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL| EMP2 | 4 | 104 | 3 (0)| 00:00:01 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("B"."ENAME"="A"."ENAME" AND "B"."JOB"="A"."JOB" AND "B"."SAL"="A"."SAL") Note ----- - dynamic sampling used for this statement (level=2)20 rows selectedSQL>

    INNER JOIN

    因為子查詢的join列(emp2.ename ,emp2.job ,emp2.sal)沒有重復行,說這個查詢可以直接改寫為inner join

    SQL> explain plan for 2 select a.ename, a.job, a.sal, a.deptno 3 from emp a join emp2 b on 4 (a.ename = b.ename 5 and a.job= b.job 6 and a.sal =b.sal);ExplainedSQL> select * from table(dbms_xplan.display());PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 166525280 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 4 | 260 | 6 (0)| 00:00:01 | |* 1 | HASH JOIN | | 4 | 260 | 6 (0)| 00:00:01 | | 2 | TABLE ACCESS FULL| EMP2 | 4 | 104 | 3 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL| EMP | 14 | 546 | 3 (0)| 00:00:01 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("A"."ENAME"="B"."ENAME" AND "A"."JOB"="B"."JOB" AND "A"."SAL"="B"."SAL") Note ----- - dynamic sampling used for this statement (level=2)20 rows selectedSQL>

    結(jié)論

    或許與大家想象的不一樣,以上三個PLAN中join寫法利用了hash join(哈希連接),其他兩種運用的是 hash join semi(哈希半連接) 。 說明在這個語句中 in 和 exists的效率是一樣的。

    所以,在不知道哪種寫法高效時應該查看Plan,而不是去記固定的結(jié)論。


    3.5 INNER JOIN、LEFT JOIN、RIGHT JOIN 和 FULL JOIN 解析

    問題

    有人對這幾種連接方式,特別是left join 和 right join 分不清楚,下面通過案例來分析一下。

    解決方案

    SQL>CREATE TABLE L AS SELECT 'left_1' AS str,'1' AS v FROM dual UNION ALL SELECT 'left_2','2' AS v FROM dual UNION ALL SELECT 'left_3','3' AS v FROM dual UNION ALL SELECT 'left_4','4' AS v FROM dual;/*右表*/ SQL>CREATE TABLE R AS SELECT 'right_3' AS str,'3' AS v,1 AS status FROM dual UNION ALL SELECT 'right_4' AS str,'4' AS v,0 AS status FROM dual UNION ALL SELECT 'right_5' AS str,'5' AS v,0 AS status FROM dual UNION ALL SELECT 'right_6' AS str,'6' AS v,0 AS status FROM dual; SQL> select * from l ;STR V ------ - left_1 1 left_2 2 left_3 3 left_4 4SQL> select * from r;STR V STATUS ------- - ---------- right_3 3 1 right_4 4 0 right_5 5 0 right_6 6 0SQL>

    inner join的特點

    該方式返回兩表相匹配的數(shù)據(jù)。

    inner join寫法: select l.str, r.str from l inner join r on l.v = r.v order by 1, 2;where寫法: select l.str, r.str from l, r where l.v = r.v order by 1, 2;輸出: STR STR ------ ------- left_3 right_3 left_4 right_4

    left join的特點

    該方式以左表為主表,左表返回所有的數(shù)據(jù),右表只返回與左表匹配的數(shù)據(jù)。

    SQL> select l.str, r.str from l left join r on l.v = r.v order by 1, 2;STR STR ------ ------- left_1 left_2 left_3 right_3 left_4 right_4SQL>

    (+)寫法:

    SQL> select l.str, r.str from l, r where l.v = r.v(+) order by 1, 2;STR STR ------ ------- left_1 left_2 left_3 right_3 left_4 right_4SQL>

    right join的特點

    該方式以右表為主表,右表返回所有的數(shù)據(jù),左表只返回與左表匹配的數(shù)據(jù)。

    select l.str, r.str from l right join r on l.v = r.v order by 1, 2;(+)寫法: select l.str, r.str from l, r where l.v(+) = r.v order by 1, 2;SQL> select l.str, r.str from l right join r on l.v = r.v order by 1, 2;STR STR ------ ------- left_3 right_3 left_4 right_4right_5right_6SQL> select l.str, r.str from l, r where l.v(+) = r.v order by 1, 2;STR STR ------ ------- left_3 right_3 left_4 right_4right_5right_6SQL>

    full join的特點

    該方式的左表和右表都返回所有的數(shù)據(jù),但只有相匹配的數(shù)據(jù)顯示在同一行,非匹配的行只顯示一個表的數(shù)據(jù)。

    SQL> select l.str, r.str from l full join r on l.v = r.v order by 1, 2;STR STR ------ ------- left_1 left_2 left_3 right_3 left_4 right_4right_5right_66 rows selectedSQL>

    注意 FULL JOIN沒有(+)寫法


    3.6 自關(guān)聯(lián)

    問題

    表emp中有個字段 mgr,是主管的編碼(對應于emp.empno)

    如何根據(jù)這個信息返回主管的姓名呢?

    解決方案

    自關(guān)聯(lián),也就是兩次查詢表emp,分別取不同的別名,這樣就可以當做是兩個 表,后面的任務就是將這兩個表 join連接起來即可。

    為了便于理解,這里我們使用漢字作為別名,并把相關(guān)列一起返回。

    select 員工.empno as 員工編碼 , 員工.ename as 員工name, 員工.job as 工作, 員工.mgr as 員工表主管編碼, 主管.empno as 主管表主管編碼, 主管.ename as 主管姓名 from emp 員工 left join emp 主管 on (員工.mgr = 主管.empno) order by 1;

    結(jié)論

    上述的操作,可以理解為我們是在兩個不同的數(shù)據(jù)集中取數(shù)據(jù)。

    create or replace view 員工 as select * from emp ; create or replace view 主管 as select * from emp ; select 員工.empno as 員工編碼 , 員工.ename as 員工name, 員工.job as 工作, 員工.mgr as 員工表主管編碼, 主管.empno as 主管表主管編碼, 主管.ename as 主管姓名 from 員工 left join 主管 on (員工.mgr = 主管.empno) order by 1;

    3.7 NOT IN、NOT EXISTS 和 LEFT JOIN

    問題

    有些單位的部門如40中一個員工也沒有,只是設置了一個部門名字,如何通過關(guān)聯(lián)查詢把這些信息查詢出來呢?

    解決方案

    數(shù)據(jù)庫版本 11.2.0.4.0

    alter table dept add constraints pk_dept primary key(deptno)

    執(zhí)行以下SQL并查詢執(zhí)行計劃

    not in

    explain plan for select * from dept where deptno not in (select deptno from emp where deptno is not null);

    not exists

    explain plan for select * from dept where not exists (select null from emp where emp.deptno = dept.deptno)

    left join

    Left join 取出的是左表中所有的數(shù)據(jù),其中右表不匹配的就表示左表not in 右表

    explain plan for select dept.* from dept left join emp on emp.deptno = dept.deptno where emp.deptno is null ;select * from table(dbms_xplan.display());

    結(jié)論

    三個PLAN應用的都是 MERGE JOINANTI,說明這三種方法的效率是一樣的。 

    若果想改寫,那么就要比對前后的PLAN,根據(jù)PLAN來判斷并測試哪種方法的效率高,而不是憑借某些結(jié)論來碰運氣。


    3.8 外連接中的條件不要亂放

    問題

    對于左連語句,見下面的數(shù)據(jù)

    SQL> select l.str, r.str ,r.status from l left join r on l.v = r.v order by 1, 2;STR STR STATUS ------ ------- ------- left_1 left_2 left_3 right_3 1 left_4 right_4 0SQL>

    對于L表,4條數(shù)據(jù)全部返回了,而對于R表,我們?nèi)绻?只需要顯示 status=1的部分,該如何寫SQL呢?

    常見的錯誤寫法, 會有人直接在上面的語句中加入條件 status=1

    select l.str, r.str ,r.status from l left join r on l.v = r.v where r.status=1order by 1, 2;

    我們來看下返回結(jié)果:

    STR STR STATUS ------ ------- ------- left_3 right_3 1

    很明顯這不是我們想要的數(shù)據(jù)集。這是很多人寫查詢或者改查詢時常遇到的一種錯誤, 問題在于所加條件的位置及寫法。

    那該如何做呢?

    解決方案

    left join寫法

    select l.str, r.str, r.statusfrom lleft join ron (l.v = r.v and r.status = 1)order by 1, 2;

    (+)寫法

    select l.str, r.str, r.statusfrom l, rwhere l.v = r.v(+)and r.status(+) = 1order by 1, 2;

    3.9 檢測兩個表中的數(shù)據(jù)及對應數(shù)據(jù)的條數(shù)是否相同

    問題

    查找視圖V 和 emp表中不同的數(shù)據(jù)

    我們先創(chuàng)建一個視圖

    create or replace view v as select * from emp where deptno !=10union all select * from emp where ename='SCOTT';

    我們可以知道 視圖V中,SCOTT有兩條記錄, EMP中有一條

    SQL> select * from v where ename='SCOTT';EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO ----- ---------- --------- ----- ----------- --------- --------- ------7788 SCOTT ANALYST 7566 1987-04-19 3000.00 207788 SCOTT ANALYST 7566 1987-04-19 3000.00 20SQL> select * from emp where ename='SCOTT';EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO ----- ---------- --------- ----- ----------- --------- --------- ------7788 SCOTT ANALYST 7566 1987-04-19 3000.00 20SQL>

    比較兩個數(shù)據(jù)集的不同時,通常類似下面的FULL JOIN 語句。

    SQL> select v.EMPNO, v.ENAME, emp.empno, emp.ename2 from v3 full join emp4 on v.EMPNO = emp.empno5 where (v.EMPNO is null or emp.empno is null);EMPNO ENAME EMPNO ENAME ----- ---------- ----- ----------7782 CLARK7839 KING7934 MILLERSQL>

    但是這種語句查不到 SCOTT的區(qū)別 。

    解決方案

    增加一列顯示相同數(shù)據(jù)的條數(shù),再進行比較。

    select v_new.empno,v_new.ENAME,v_new.cnt,emp_new.empno,emp_new.ename,emp_new.cntfrom (select v.empno, v.ENAME, count(1) as cntfrom vgroup by v.empno, v.ENAME) v_newfull join (select a.empno, a.ENAME, count(1) as cntfrom emp agroup by a.empno, a.ENAME) emp_newon (v_new.EMPNO = emp_new.empno and v_new.cnt = emp_new.cnt)where (v_new.EMPNO is null or emp_new.empno is null);

    結(jié)果集:

    EMPNO ENAME CNT EMPNO ENAME CNT ----- ---------- ---------- ----- ---------- ----------7788 SCOTT 17934 MILLER 17782 CLARK 17839 KING 17788 SCOTT 2 SQL>

    3.10 聚集與內(nèi)連接

    問題

    解決方案

    結(jié)論


    3.11 聚集與外連接

    問題

    解決方案

    結(jié)論


    3.12 從多個表中返回丟失的數(shù)據(jù)

    問題

    同時返回多個表中丟失的數(shù)據(jù)。 要從DEPT中返回EMP不存在的行(所有沒有員工的部門)需要做外連接。

    首先我們在EMP中增加一行deptno為空的數(shù)據(jù),如下:

    insert into emp(empno, ename, job, mgr, hiredate, sal, comm, deptno)select 6666, 'XGJ', 'JEDI', null, hiredate, sal, comm, nullfrom empwhere ename = 'KING';commit ;

    此時,我們來看下 我們要查詢的表中數(shù)據(jù):


    這時,如果我們使用下面的語句關(guān)聯(lián)查詢 ,就會發(fā)現(xiàn)少了emp=6666和 deptno=40的數(shù)據(jù)

    select e.empno, e.ename, b.deptno, b.dnamefrom emp ejoin dept bon e.deptno = b.deptno;

    如果想要返回這兩條數(shù)據(jù)該如何寫查詢語句呢? 下面介紹兩種方法

    解決方案

    full join

    select e.empno, e.ename, b.deptno, b.dnamefrom emp efull join dept bon e.deptno = b.deptno;

    union all

    select e.empno, e.ename, b.deptno, b.dnamefrom emp eleft join dept bon e.deptno = b.deptnounion allselect e.empno, e.ename, b.deptno, b.dnamefrom emp eright join dept bon e.deptno = b.deptno where e.empno is null ;

    在這里不建議使用union ,因為union會去掉重復記錄。 如果確定需要去掉重復記錄再使用。


    3.13 多表查詢時的空值處理

    問題

    NULL值永遠不會等于或者不等于任何值,也包括null自己,但是需要像計算真實值一樣計算可為空列的返回值。

    返回所有比ALLEN提成低的員工, 提成 comm字段 ,有空值 。

    數(shù)據(jù)如下:

    SQL> select * from emp;EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO ----- ---------- --------- ----- ----------- --------- --------- ------7369 SMITH CLERK 7902 1980-12-17 800.00 207499 ALLEN SALESMAN 7698 1981-02-20 1600.00 300.00 307521 WARD SALESMAN 7698 1981-02-22 1250.00 500.00 307566 JONES MANAGER 7839 1981-04-02 2975.00 207654 MARTIN SALESMAN 7698 1981-09-28 1250.00 1400.00 307698 BLAKE MANAGER 7839 1981-05-01 2850.00 307782 CLARK MANAGER 7839 1981-06-09 2450.00 107788 SCOTT ANALYST 7566 1987-04-19 3000.00 207839 KING PRESIDENT 1981-11-17 5000.00 107844 TURNER SALESMAN 7698 1981-09-08 1500.00 0.00 307876 ADAMS CLERK 7788 1987-05-23 1100.00 207900 JAMES CLERK 7698 1981-12-03 950.00 307902 FORD ANALYST 7566 1981-12-03 3000.00 207934 MILLER CLERK 7782 1982-01-23 1300.00 1014 rows selectedSQL>

    我們來看個錯誤的寫法:

    SQL> select a.ename , a.comm from emp a where a.comm < (select comm from emp a where a.ename = 'ALLEN');ENAME COMM ---------- --------- TURNER 0.00SQL>

    只返回了一條 TURNER的數(shù)據(jù),comm有許多空值 的并沒有被顯示出來,原因在于與空值比較后結(jié)果還是空值,需要先轉(zhuǎn)換才行

    解決方案

    使用coalesce函數(shù)將null值轉(zhuǎn)換為一個可以用來作為標準值進行比較的真實值。

    coalesce函數(shù)從值列表中返回第一個非NULL值。當遇到NULL值將其替換為0,這樣就可以同ALLEN的提成進行比較了。

    SQL> select a.ename , a.comm from emp a where coalesce( a.comm,0 ) < (select comm from emp a where a.ename = 'ALLEN');ENAME COMM ---------- --------- SMITH JONES BLAKE CLARK SCOTT KING TURNER 0.00 ADAMS JAMES FORD MILLER 11 rows selectedSQL>

    或者

    select a.ename , a.comm from emp a where nvl( a.comm,0 ) < (select comm from emp a where a.ename = 'ALLEN');

    總結(jié)

    以上是生活随笔為你收集整理的Oracle查询优化-03操作多个表的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。