天天看點

oracle常見的連接配接方式

隻有對這些問題有了清晰的了解後,我們才能針對特定的查詢需求選擇合适的連接配接方式,開發出健壯的

資料庫

應用程式。選擇合适的表連接配接方法對

SQL

語句運作的性能有着至關重要的影響。下面我們就Oracle常用的一些連接配接方法及适用情景做一個簡單的介紹。

一、嵌套循環連接配接( Nested Loop

嵌套循環連接配接的工作方式是這樣的:

1、Oracle首先選擇一張表作為連接配接的驅動表,這張表也稱為外部表(Outer

Table

)。由驅動表進行驅動連接配接的表或資料源稱為内部表(Inner Table)。

2、提取驅動表中符合條件的記錄,與被驅動表的連接配接列進行關聯查詢符合條件的記錄。在這個過程中,Oracle首先提取驅動表中符合條件的第一條記錄,再與内部表的連接配接列進行關聯查詢相應的記錄行。在關聯查詢的過程中,Oracle會持續提取驅動表中其他符合條件的記錄與内部表關聯查詢。這兩個過程是并行進行的,是以嵌套循環連接配接傳回前幾條記錄的速度是非常快的。在這裡需要說明的是,由于Oracle最小的IO機關為單個資料塊,是以在這個過程中 Oracle會首先提取驅動表中符合條件的單個資料塊中的所有行,再與内部表進行關聯連接配接查詢的,然後提取下一個資料塊中的記錄持續地循環連接配接下去。當然,如果單行記錄跨越多個資料塊的話,就是一次單條記錄進行關聯查詢的。

3、嵌套循環連接配接的過程如下所示:

NESTED LOOP

<Outer Loop>

<Inner Loop >

我們可以看出這裡面存在着兩個循環,一個是外部循環,提取驅動表中符合條件的每條記錄。另外一個是内部循環,根據外循環中提取的每條記錄對内部表進行連接配接查詢相應的記錄。由于這兩個循環是嵌套進行的,故此種連接配接方法稱為嵌套循環連接配接。

嵌套循環連接配接适用于查詢的選擇性強、限制性高并且僅傳回小部分記錄的結果集。通常要求驅動表的記錄(符合條件的記錄,通常通過高效的索引通路)較少,且被驅動表連接配接列有唯一索引或者選擇性強的非唯一索引時,嵌套循環連接配接的效率是比較高的。比如下面這個查詢是選用嵌套循環連接配接的典型例子:

SQL> select e.empno,e.ename,e.job,d.dname

2 from emp e,dept d

3 where e.deptno=d.deptno

4 and e.empno=7900;

EMPNO ENAME JOB DNAME

---------- ---------- --------- --------------

7900 JAMES CLERK SALES

Execution Plan

----------------------------------------------------------

0 SELECT STATEMENT ptimizer=CHOOSE

1 0 NESTED LOOPS

2 1 TABLE ACCESS (BY INDEX ROWID) OF 'EMP'

3 2 INDEX (UNIQUE SCAN) OF 'PK_EMP' (UNIQUE)

4 1 TABLE ACCESS (BY INDEX ROWID) OF 'DEPT'

5 4 INDEX (UNIQUE SCAN) OF 'PK_DEPT' (UNIQUE)

在這個查詢中,優化器選擇emp作為驅動表,根據唯一性索引PK_EMP快速傳回符合條件empno為7900的記錄,然後再與被驅動表dept的 deptno關聯查詢相應的dname并最終傳回結果集。由于dept表上面的deptno有唯一索引PK_DEPT,故查詢能夠快速地定位deptno 對應dname為SALES的記錄并傳回。

嵌套循環連接配接驅動表的選擇也是連接配接中需要着重注意的一點,有一個常見的誤區是驅動表要選擇小表,其實這是不對的。假如有兩張表A、B關聯查詢,A表有1000000條記錄,B表有10000條記錄,但是A表過濾出來的記錄隻有10條,這時候顯然用A表當做驅動表是比較合适的。是以驅動表是由過濾條件限制傳回記錄最少的那張表,而不是根據表的大小來選擇的。

在外連接配接查詢中,如果走嵌套循環連接配接的話,那麼驅動表必然是沒有符合條件關聯的那張表,也就是後面不加(+)的那張表。這是由于外連接配接需要提取可能另一張表沒符合條件的記錄,是以驅動表需要是那張我們要傳回所有符合條件記錄的表。比如下面這個查詢,就是選擇了emp表做為驅動表進行連接配接:

[email protected]> select emp.ename,dept.dname

2  from emp, dept

3   where emp.deptno=dept.deptno(+);

ENAME      DNAME

---------- --------------

SMITH

ALLEN

WARD       SALES

JONES      RESEARCH

MARTIN     SALES

BLAKE      SALES

CLARK      ACCOUNTING

SCOTT      RESEARCH

KING       ACCOUNTING

TURNER     SALES

ADAMS       RESEARCH

JAMES      SALES

FORD       RESEARCH

MILLER     ACCOUNTING

14 rows selected.

Execution Plan

----------------------------------------------------------

|   0 | SELECT STATEMENT             |         |    14 |   308 |    15

|   1 |  NESTED LOOPS OUTER          |         |    14 |   308 |    15

|   2 |   TABLE ACCESS FULL          | EMP     |    14 |   126 |     3

|   3 |   TABLE ACCESS BY INDEX ROWID| DEPT    |     1 |    13 |     1

|*  4 |    INDEX UNIQUE SCAN         | DEPT_PK |     1 |       |     0

嵌套循環連接配接傳回前幾行的記錄是非常快的,這是因為使用了嵌套循環後,不需要等到全部循環結束再傳回結果集,而是不斷地将查詢出來的結果集傳回。在這種情況下,終端使用者将會快速地得到傳回的首批記錄,且同時等待Oracle内部處理其他記錄并傳回。如果查詢的驅動表的記錄數非常多,或者被驅動表的連接配接列上無索引或索引不是高度可選的情況,嵌套循環連接配接的效率是非常低的。

二、排序合并連接配接 (Sort Merge)

排序合并連接配接的方法非常簡單。在排序合并連接配接中是沒有驅動表的概念的,兩個互相連接配接的表按連接配接列的值先排序,排序完後形成的結果集再互相進行合并連接配接提取符合條件的記錄。相比嵌套循環連接配接,排序合并連接配接比較适用于傳回大資料量的結果。以下為排序合并連接配接的例子:

[email protected]> select emp.ename,dept.dname

2 from emp, dept

3 where emp.deptno=dept.deptno

4 /

ENAME DNAME

---------- --------------

CLARK ACCOUNTING

KING ACCOUNTING

MILLER ACCOUNTING

JONES RESEARCH

SCOTT RESEARCH

FORD RESEARCH

ADAMS RESEARCH

TURNER SALES

JAMES SALES

WARD SALES

MARTIN SALES

BLAKE SALES

12 rows selected.

Execution Plan

---------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

---------------------------------------------------------------

| 0 | SELECT STATEMENT | | 12 | 264 | 8 (25)| 00:00:01 |

| 1 | MERGE JOIN | | 12 | 264 | 8 (25)| 00:00:01 |

| 2 | SORT JOIN | | 4 | 52 | 4 (25)| 00:00:01 |

| 3 |

TABLE

ACCESS FULL| DEPT | 4 | 52 | 3 (0)| 00:00:01 |

|* 4 | SORT JOIN | | 12 | 108 | 4 (25)| 00:00:01 |

|* 5 | TABLE ACCESS FULL| EMP | 12 | 108 | 3 (0)| 00:00:01 |

可以看得出來上述查詢首先按dept、emp兩張表的deptno先排序,然後排序好的結果集再進行合并連接配接傳回最終的記錄。

排序合并連接配接在資料表預先排序好的情況下效率是非常高的,也比較适用于非等值連接配接的情況,比如>、>=、<=等情況下的連接配接(哈希連接配接隻适用于等值連接配接)。由于Oracle中排序操作的開銷是非常消耗資源的,當結果集很大時排序合并連接配接的性能很差,于是Oracle在7.3之後推出了新的連接配接方式——哈希連接配接。

三、哈希連接配接( Hash join

哈希連接配接分為兩個階段,如下。

1、建構階段:優化器首先選擇一張小表做為驅動表,運用哈希函數對連接配接列進行計算産生一張哈希表。通常這個步驟是在記憶體(hash_area_size)裡面進行的,是以運算很快。

2、探測階段:優化器對被驅動表的連接配接列運用同樣的哈希函數計算得到的結果與前面形成的哈希表進行探測傳回符合條件的記錄。這個階段中如果被驅動表的連接配接列的值沒有與驅動表連接配接列的值相等的話,那麼這些記錄将會被丢棄而不進行探測。關于哈希連接配接更深層次的原理可以參考

Itpub

上網友logzgh發表的“hash join算法原理”文章(http://www.itpub.net/showthread.php?threadid=315494)。

以下為哈希連接配接的一個例子:

[email protected]> select emp.ename,dept.dname

2  from emp, dept

3   where emp.deptno=dept.deptno;

ENAME      DNAME

---------- --------------

WARD       SALES

JONES      RESEARCH

MARTIN     SALES

BLAKE      SALES

CLARK      ACCOUNTING

SCOTT      RESEARCH

KING       ACCOUNTING

TURNER     SALES

ADAMS      RESEARCH

JAMES      SALES

FORD       RESEARCH

MILLER     ACCOUNTING

12 rows selected.

Execution Plan

---------------------------------------------------------------------------

| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |

---------------------------------------------------------------------------

|   0 | SELECT STATEMENT   |      |    12 |   264 |     7  (15)| 00:00:01 |

|*  1 |  HASH JOIN         |      |    12 |   264 |     7  (15)| 00:00:01 |

|   2 |   TABLE ACCESS FULL| DEPT |     4 |    52 |     3   (0)| 00:00:01 |

|*  3 |   TABLE ACCESS FULL| EMP  |    12 |   108 |     3   (0)| 00:00:01 |

在這個查詢中優化器首先選擇dept這張表為驅動表,對列deptno運算哈希函數建構一張哈希表,然後再對被驅動表emp的deptno列運算同樣的哈希函數計算得到的結果進行探測,最終連接配接得出符合條件的記錄。

同嵌套循環外連接配接一樣,哈希循環外連接配接的驅動表同樣是沒有符合條件關聯的那張表。如下述例子:

[email protected]> select emp.ename,dept.dname

2 from emp,dept

3 where emp.deptno=dept.deptno(+);

ENAME DNAME

---------- --------------

MILLER ACCOUNTING

KING ACCOUNTING

CLARK ACCOUNTING

FORD RESEARCH

ADAMS RESEARCH

SCOTT RESEARCH

JONES RESEARCH

JAMES SALES

TURNER SALES

BLAKE SALES

MARTIN SALES

WARD SALES

ALLEN

SMITH

14 rows selected.

Execution Plan

--------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

--------------------------------------------------------------

| 0 | SELECT STATEMENT | | 14 | 308 | 7 (15)| 00:00:01 |

|* 1 | HASH JOIN OUTER | | 14 | 308 | 7 (15)| 00:00:01 |

| 2 | TABLE ACCESS FULL| EMP | 14 | 126 | 3 (0)| 00:00:01 |

| 3 | TABLE ACCESS FULL| DEPT | 4 | 52 | 3 (0)| 00:00:01 |

--------------------------------------------------------------

哈希連接配接比較适用于傳回大資料量結果集的連接配接。使用哈希連接配接必須是在CBO模式下,參數hash_join_enabled設定為true,且隻适用于等值連接配接。從Oracle9i開始,哈希連接配接由于其良好的性能漸漸取代了原來的排序合并連接配接。

四、跟表連接配接有關的幾個 HINT

(1)use_nl(t1,t2):表示對表t1、t2關聯時采用嵌套循環連接配接。

(2)use_merge(t1,t2):表示對表t1、t2關聯時采用排序合并連接配接。

(3)use_hash(t1,t2):表示對表t1、t2關聯時采用哈希連接配接。

(4)leading(t):表示在進行表連接配接時,選擇t為驅動表。

(5)ordred:要求優化器按from列出的表順序進行連接配接。

需要注意的是在Oracle使用hint時,如果

SQL

語句中表用别名的話,那麼hint中必須使用表的别名,否則hint将不會生效。