Oracle查詢轉換和SQL連接方式

Oracle查詢轉換和SQL連接方式

 

一、表連接方法:

在Oracle數據庫中,幾個表甚至十幾個表相互關聯的語句隨處可見,這就需要Oracle提供一整套的表連接方案來支持這種類型的SQL,實際上不管多少個表的連接,Oracle都只能先處理兩個表的連接,Oracle會通過優化器依據統計信息選出驅動表和被驅動表,然後完成第一次兩表關聯,依此類推直至完成整條SQL的表連接。關於Oracle提供的表連接方式有排序合併連接(Sort Merge Join)、嵌套輪換連接(Nested Loops Join)、哈希連接(Hash Join)和笛卡兒連接(Cross Join)四種,其中排序合併和哈希連接是Oracle在10g之後才提供的。

 

1.排序合併連接(Sort Merge Join)

顧名思義排序合併連接就是將兩個表(T1表和T2表)先對結果集進行排序,然後在進行結果集合並。

它的執行過程大致應該是這樣的:

①首先對T1表根據謂詞條件查詢出目標結果集並進行排序,記錄爲結果集1。

②然後對T2表根據謂詞條件查詢出目標結果集並進行排序,記錄爲結果集2。

③最後在把結果集1和結果集2進行合併,爲最終結果集。其中這部分操作應該是這樣的,首先取出結果集1的第一條記錄數在結果集2中進行遍歷,由於已經做了排序操作,所以不需要掃描全部結果集,只需要遍歷目標數的結果集即可,刪除遍歷到的結果集2中的記錄數,然後從結果集2選取第二條記錄數依此遍歷。

sort mrege join原理:

BEGIN

FOR OUTER IN (SELECT * FROM PGA裏排序好的emp)

LOOP -- outer loop

FOR INNER IN (SELECT * FROM PGA裏排序好的dept

WHERE dept.deptno = emp.deptno)

LOOP -- inner loop

dbms_output.put_line( ... );

END LOOP;

END LOOP;

END;

 

執行計劃:

SQL> select /*+ use_merge(a)*/ a.* ,b.loc

2 from scott.emp a ,scott.dept b

3 where a.deptno=b.deptno

4 and empno>'1500';



14 rows selected.





Execution Plan

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

Plan hash value: 817787759



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

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

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

| 0 | SELECT STATEMENT | | 14 | 686 | 5 (20)| 00:00:01 |

| 1 | MERGE JOIN | | 14 | 686 | 5 (20)| 00:00:01 |

| 2 | TABLE ACCESS BY INDEX ROWID | DEPT | 4 | 44 | 2 (0)| 00:00:01 |

| 3 | INDEX FULL SCAN | PK_DEPT | 4 | | 1 (0)| 00:00:01 |

|* 4 | SORT JOIN | | 14 | 532 | 3 (34)| 00:00:01 |

| 5 | TABLE ACCESS BY INDEX ROWID| EMP | 14 | 532 | 2 (0)| 00:00:01 |

|* 6 | INDEX RANGE SCAN | PK_EMP | 14 | | 1 (0)| 00:00:01 |

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



Predicate Information (identified by operation id):

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



4 - access("A"."DEPTNO"="B"."DEPTNO")

filter("A"."DEPTNO"="B"."DEPTNO")

6 - access("EMPNO">1500)





Statistics

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

1 recursive calls

0 db block gets

6 consistent gets

0 physical reads

0 redo size

1689 bytes sent via SQL*Net to client

524 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

1 sorts (memory)

0 sorts (disk)

14 rows processed

總結:

①對於處理海量數據有較好的支撐,處理過程中outer 和inter只掃描一次到buffer cache中,不可避免存在latch爭用,其中的sort操作需要大量消耗pga。

②使用範圍比hash join更爲廣泛,可以在不等式連接中使用。

③連接過程不太依賴索引,但是對於索引排序效果會更快。

 

2.嵌套輪換連接(Nested Loops Join)

嵌套連接實際上就是if else循環,其中存在驅動表和被驅動表的概念,Oracle會根據一定的規則選擇驅動表(在沒有SQL指定的情況下Oracle默認選擇結果集數較少的表爲驅動表,若有SQL指定例如hint,則根據SQL指定來選擇具體驅動表)

它的執行過程大致應該是這樣的:

①首先Oracle會根據統計信息和實際的SQL語句判斷驅動表與被驅動表選擇。

②對驅動表T1根據謂詞條件查詢出目標結果集,記錄爲結果集1。

③對被驅動表T2根據謂詞條件查詢出目標結果集,記錄爲結果集2。

④遍歷結果集1,並將遍歷的記錄數依次在結果集2中遍歷,得出最終結果集。實際過程應該是這樣的,從結果集1中遍歷第一條數,然後在結果集2中全部遍歷得到結果,然後選擇結果集1中的第二條數依此遍歷,這個過程需要對結果集1(驅動表)多次遍歷。

netsted loop join 連接原理:

< C , JAVA >

FOR (i = 0; i<100; i++){ -- outer loop

FOR (j = 0; j<100; j++) { -- inner loop

// Do Anything ...

}

}

 

< PL/SQL >

FOR OUTER IN 1..100 LOOP

FOR INNER IN 1..100 LOOP

dbms_output.put_line(OUTER || ' : ' || INNER);

END LOOP;

END LOOP

 

PL/SQL代碼示例:

 

BEGIN

FOR OUTER IN (SELECT emp.* FROM emp

where emp.empno>'1500')

LOOP -- outer loop 外循環

FOR INNER IN (SELECT dept.loc FROM dept

WHERE dept.deptno = emp.deptno)

LOOP -- inner loop 內循環

dbms_output.put_line(

outer.職員名 || ' : ' || INNER.客戶名 || ' : ' || INNER.電話號);

END LOOP;

END LOOP;

END;

 

執行計劃:

SQL> select /*+ leading(a) use_nl(b)*/ a.* ,b.loc

2 from scott.emp a ,scott.dept b

3 where a.deptno=b.deptno

4 and a.empno>'1500';



14 rows selected.





Execution Plan

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

Plan hash value: 1465018025



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

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

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

| 0 | SELECT STATEMENT | | 14 | 686 | 16 (0)| 00:00:01 |

| 1 | NESTED LOOPS | | 14 | 686 | 16 (0)| 00:00:01 |

| 2 | NESTED LOOPS | | 14 | 686 | 16 (0)| 00:00:01 |

| 3 | TABLE ACCESS BY INDEX ROWID| EMP | 14 | 532 | 2 (0)| 00:00:01 |

|* 4 | INDEX RANGE SCAN | PK_EMP | 14 | | 1 (0)| 00:00:01 |

|* 5 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 0 (0)| 00:00:01 |

| 6 | TABLE ACCESS BY INDEX ROWID | DEPT | 1 | 11 | 1 (0)| 00:00:01 |

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



Predicate Information (identified by operation id):

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



4 - access("EMPNO">1500)

5 - access("A"."DEPTNO"="B"."DEPTNO")





Statistics

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

1 recursive calls

0 db block gets

22 consistent gets

0 physical reads

0 redo size

1748 bytes sent via SQL*Net to client

524 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

14 rows processed



總結

①若驅動表小(結果集小)同時被驅動表的連接列上存在唯一性索引或者選擇性較好的索引,那麼嵌套循環是非常高效的。反之,若驅動表比較大,即使被驅動表存在選擇性好的索引,那麼嵌套循環的效率也不好。

②嵌套循環可以實現快速響應,即第一時間先返回已經連接過且滿足連接條件的記錄,這一點在排序合併連接和哈希連接都是做不到的。

③依賴於索引,需要對驅動表多次掃描,需要讀取數據表塊到buffer cache中,且讀取被驅動表都需要以單塊讀的方式讀取到buffer cache中(在11g中引入了向量IO,把一批需要單塊讀的IO組合起來,使用一個向量IO代替),不可避免存在latch爭用。

④對於海量數據處理效果不佳

 

 

3.哈希連接(Hash Join)

哈希連接主要依靠哈希運算實現的,是在Oracle10g以後才引入的,由參數_HASH_JOIN_ENABLED參數控制,默認爲TRUE。

它的工作方式比較複雜,大致爲:

①Oracle根據參數HASH_AREA_SIZE,DB_BLOCK_SIZE和_HASH_MULTIBLOCK_IO_COUNT參數值來確定Hash partition的數量(一組hash bucket組成一個hash partition,一組hash partition組成一個hash table),oracle 根據以上原理生成hash表(build 階段)。

②Oracle分別對錶T1和T2生成的hash表根據謂詞條件查詢出目標結果集,選擇小結果集(A)爲驅動結果集,並對此結果集(A)進行遍歷,此過程發生在pga的work area中,若pga不夠會使用temp表空間,如果結果集A的數據量很大,那麼就會出現pga工作區填滿的情況(Probe 階段)。

③同樣的方法遍歷結果集B,並根據已經遍歷出來的結果集A查找與結果集B相同的結果集,記錄爲最終結果集。

hash join原理(Probe 階段):

BEGIN

FOR OUTER IN (SELECT emp.* FROM emp

where emp.empno>'1500')

LOOP -- outer loop

FOR INNER IN (SELECT dept.loc

FROM dept的PGA裏的生成的Hash表

WHERE dept.deptno = emp.deptno)

LOOP -- inner loop

dbms_output.put_line( ... );

END LOOP;

END LOOP;

END;

 

執行計劃:

SQL> select /*+ use_hash(a)*/ a.* ,b.loc

2 from scott.emp a ,scott.dept b

3 where a.deptno=b.deptno

4 and empno>'1500';



14 rows selected.





Execution Plan

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

Plan hash value: 1505574247



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

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

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

| 0 | SELECT STATEMENT | | 14 | 686 | 5 (0)| 00:00:01 |

|* 1 | HASH JOIN | | 14 | 686 | 5 (0)| 00:00:01 |

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

| 3 | TABLE ACCESS BY INDEX ROWID| EMP | 14 | 532 | 2 (0)| 00:00:01 |

|* 4 | INDEX RANGE SCAN | PK_EMP | 14 | | 1 (0)| 00:00:01 |

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



Predicate Information (identified by operation id):

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



1 - access("A"."DEPTNO"="B"."DEPTNO")

4 - access("EMPNO">1500)





Statistics

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

1 recursive calls

0 db block gets

10 consistent gets

0 physical reads

0 redo size

1748 bytes sent via SQL*Net to client

524 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

14 rows processed

總結:

①hash join不一定會進行排序,大部分情況下是不需要排序的。

②hash join 僅適用於CBO,且在選擇的表爲小表和大表之間的連接性能尤爲突出。

③連接過程不依賴索引,從pga的hash表中讀取數據在buffer cache中構建查詢表,不可避免存在latch爭用。對於小表直接在pga中進行處理。

 

4.笛卡兒連接(Cross Join)

笛卡兒連接又稱爲笛卡兒乘積,實在兩個表沒有執行表連接時產生的。

它的執行順序是這樣的:

①首先對錶T1根據謂詞條件查詢出目標結果集,記錄爲結果集1記錄數爲m。

②然後對錶T2根據謂詞條件查詢出目標結果集,記錄爲結果集2記錄數爲n。

③最後把結果集1和結果集2進行合併(m*n)

 

執行計劃:

SQL> select count(*) from scott.emp;



COUNT(*)

----------

14



SQL> select count(*) from scott.dept;



COUNT(*)

----------

4 --結果集爲14*4=56



SQL> select a.* ,b.loc

2 from scott.emp a ,scott.dept b ;



56 rows selected.





Execution Plan

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

Plan hash value: 2034389985



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

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

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

| 0 | SELECT STATEMENT | | 56 | 2576 | 10 (0)| 00:00:01 |

| 1 | MERGE JOIN CARTESIAN| | 56 | 2576 | 10 (0)| 00:00:01 |

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

| 3 | BUFFER SORT | | 14 | 532 | 7 (0)| 00:00:01 |

| 4 | TABLE ACCESS FULL | EMP | 14 | 532 | 2 (0)| 00:00:01 |

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





Statistics

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

0 recursive calls

0 db block gets

15 consistent gets

0 physical reads

0 redo size

3914 bytes sent via SQL*Net to client

557 bytes received via SQL*Net from client

5 SQL*Net roundtrips to/from client

1 sorts (memory)

0 sorts (disk)

56 rows processed

 

一般出現笛卡兒連接的SQL都是有問題的SQL。

 

二、表連接類型:

1.內連接(Inner Join)

連接結果集只包含完全滿足條件的記錄

對於SQL的傳統寫法如下:

select t1.col1,t2.col2,t2.col3

from t1,t2

where t1.col2=t2.col2;

 

使用內連接改寫爲:

select t1.col1,t2.col2,t2.col3

from t1

join t2 on (t1.col2=t2.col2);

select t1.col1, col2,t2.col3

from t1

join t2 using (col2); ---對於此方式寫法則要求連接列不能使用別名,否則會報ORA - 25154錯誤

 

其中內連接還有一種特殊的連接類型 NATURAL JOIN ,此連接類型會自動連接兩張表的同名列,不需要指定。

優點:寫法方面簡單 缺點:對於生產業務表列同名並不代表含義完全一樣,此寫法增加了SQL錯誤的風險。所以並不推薦使用此方法

select t1.col1,t2.col2,t2.col3

from t1 natural join t2 ;

 

2.外連接(Outer Join)

外連接是內連接的一種擴展,這種連接結果集除了包含完全滿足連接條件的記錄之外還會包含驅動表不滿足該條件的記錄。

左外連接: left outer join 或 left join

其中t1爲驅動表,顯示結果集包含t1和t2表結果集相交部分,還包含t1表連接記錄之外的結果集,t2.col3列沒有的行用null代替。在兩個表連接時首先先掃描t1,在使用t1的選擇結果集與t2表上逐一掃描。所以Oracle建議選擇選擇結果集小的表爲驅動表。

select t1.col1,t2.col2,t2.col3

from t1 left outer join t2

on (t1.col2=t2.tol2);

select t1.col1,t2.col2,t2.col3

from t1 , t2

where t1.col2=t2.tol2(+);

 

select t1.col1,t2.col2,t2.col3

from t1 left outer join t2

on (t1.col2=t2.tol2 and t1.col1=1);

select t1.col1,t2.col2,t2.col3

from t1 , t2

where t1.col2=t2.tol2(+)

and t1.col1 =1(+);

 

右外連接: right outer join 或 right join

其中t2爲驅動表,顯示結果集包含t1和t2表結果集相交部分,還包含t2表連接記錄之外的結果集,t1.col1列沒有的行用null代替。

select t1.col1,t2.col2,t2.col3

from t1 right outer join t2

on (t1.col2=t2.tol2);

select t1.col1,t2.col2,t2.col3

from t1 , t2

where t1.col2(+)=t2.tol2;

 

全部外連接:full outer join 或 full join

顯示結果集包含t1表和t2表關聯之後所有的結果集,對應不上的結果行用null代替。對於full outer join 可以理解成 先 left outer join 在right outer join,然後結果集union(可以這麼理解,但是實際執行不是這樣的)

 

select t1.col1,t2.col2,t2.col3

from t1 full outer join t2

on (t1.col2=t2.tol2);

 

3.反連接(Anti Join )

反連接是一種特殊的連接類型,在SQL文本中沒有特殊的關鍵字來表示反連接,這裏爲了描述方便講反連接描述爲:t1.x anti=t2.y(t1爲驅動表),含義:t2表中有滿足t1.x=t2.y條件的結果存在就會返回t1中不滿足條件的結果行。當做子查詢展開時,Oracle經常會吧外部where條件爲 not exists、not IN或<> all 的子查詢轉換成對應的反連接。

select * from t1

where col2 not in (select col2 from t2);

 

執行計劃:

SQL> select *

2 from scott.emp

3 where deptno not in (select deptno from scott.dept );



no rows selected





Execution Plan

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

Plan hash value: 1948401889



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

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

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

| 0 | SELECT STATEMENT | | 5 | 205 | 4 (0)| 00:00:01 |

|* 1 | HASH JOIN ANTI SNA| | 5 | 205 | 4 (0)| 00:00:01 |

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

| 3 | INDEX FULL SCAN | PK_DEPT | 4 | 12 | 1 (0)| 00:00:01 |

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



Predicate Information (identified by operation id):

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



1 - access("DEPTNO"="DEPTNO")





Statistics

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

1 recursive calls

0 db block gets

7 consistent gets

0 physical reads

0 redo size

799 bytes sent via SQL*Net to client

513 bytes received via SQL*Net from client

1 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

0 rows processed

總結:

在表中一旦有了null值那麼not exists、not IN或<> all久不是等價的了,not exists、not IN對null比較敏感,若子查詢中有了null,則not exists、not IN連接就沒有結果。若外部查詢存在null,那麼not exists、not IN也是不顯示null行。

 

4.半連接(Semi Join)

半連接是一種特殊的連接類型,在SQL文本中沒有特殊的關鍵字來表示半連接,這裏爲了描述方便講半連接描述爲:t1.x semi=t2.y(t1爲驅動表),含義:只要t2表中找到一條滿足t1.x=t2.y條件的結果存在,就會停止搜索t2表返回t1表中滿足條件的記錄行(只返回一行)。實際上半連接會去重。當做子查詢展開時,Oracle經常會吧外部where條件爲exists、IN或=any的子查詢轉換成對應的半連接。

select * from t1

where col2 in (select col2 from t2);

 

執行計劃:

SQL> select *

2 from scott.dept

3 where deptno in (select deptno from scott.emp );





Execution Plan

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

Plan hash value: 1090737117



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

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

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

| 0 | SELECT STATEMENT | | 3 | 69 | 6 (17)| 00:00:01 |

| 1 | MERGE JOIN SEMI | | 3 | 69 | 6 (17)| 00:00:01 |

| 2 | TABLE ACCESS BY INDEX ROWID| DEPT | 4 | 80 | 2 (0)| 00:00:01 |

| 3 | INDEX FULL SCAN | PK_DEPT | 4 | | 1 (0)| 00:00:01 |

|* 4 | SORT UNIQUE | | 14 | 42 | 4 (25)| 00:00:01 |

| 5 | TABLE ACCESS FULL | EMP | 14 | 42 | 3 (0)| 00:00:01 |

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



Predicate Information (identified by operation id):

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



4 - access("DEPTNO"="DEPTNO")

filter("DEPTNO"="DEPTNO")





Statistics

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

1 recursive calls

0 db block gets

10 consistent gets

0 physical reads

0 redo size

768 bytes sent via SQL*Net to client

524 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

1 sorts (memory)

0 sorts (disk)

3 rows processed

總結:

exists、IN或=any的結果是一樣的。

 

5.星型連接(Star Join)

星型連接一般出現在數據倉庫中,是單個事實表(fact table)和多個維度表(dimension table)之間的連接。其中多個維度表基於事實表從各個維度通過外鍵列和對應維度表的主鍵列之間建立連接,通常需要在事實表中創建位圖索引(bitmap index)<關於索引會在後邊詳細討論>,而對於此類創建了位圖索引的表Oracle強烈建議不能進行DML操作,一旦進行dml操作會大大降低位圖索引的執行效率。

SH@PROD1> set linesize 200;

SH@PROD1> set pagesize 200;

SH@PROD1> SELECT CH.CHANNEL_CLASS,C.CUST_CITY , T.CALENDAR_QUARTER_DESC,

2 SUM(S.AMOUNT_SOLD) SALES_AMOUNT

3 FROM SH.SALES S ,SH.TIMES T,SH.CUSTOMERS C,SH.CHANNELS CH

4 WHERE S.TIME_ID = T.TIME_ID

5 AND S.CUST_ID=C.CUST_ID

6 AND S.CHANNEL_ID = CH.CHANNEL_ID

7 AND C.CUST_STATE_PROVINCE ='CA'

8 AND CH.CHANNEL_DESC IN ('Internet','Catalog')

9 AND T.CALENDAR_QUARTER_DESC IN ('1999-Q1','1999-Q2')

10 GROUP BY CH.CHANNEL_CLASS,C.CUST_CITY , T.CALENDAR_QUARTER_DESC;



no rows selected





Execution Plan

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

Plan hash value: 4231244587



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

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

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

| 0 | SELECT STATEMENT | | 254 | 18542 | 557 (1)| 00:00:07 | | |

| 1 | TEMP TABLE TRANSFORMATION | | | | | | | |

| 2 | LOAD AS SELECT | SYS_TEMP_0FD9D660D_1744F2 | | | | | | |

|* 3 | TABLE ACCESS FULL | CUSTOMERS | 383 | 9958 | 405 (1)| 00:00:05 | | |

| 4 | HASH GROUP BY | | 254 | 18542 | 152 (2)| 00:00:02 | | |

|* 5 | HASH JOIN | | 254 | 18542 | 128 (1)| 00:00:02 | | |

|* 6 | TABLE ACCESS FULL | TIMES | 183 | 2928 | 18 (0)| 00:00:01 | | |

|* 7 | HASH JOIN | | 254 | 14478 | 110 (1)| 00:00:02 | | |

|* 8 | HASH JOIN | | 254 | 10668 | 107 (0)| 00:00:02 | | |

|* 9 | TABLE ACCESS FULL | CHANNELS | 2 | 42 | 3 (0)| 00:00:01 | | |

| 10 | PARTITION RANGE SUBQUERY | | 254 | 5334 | 104 (0)| 00:00:02 |KEY(SQ)|KEY(SQ)|

| 11 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 254 | 5334 | 104 (0)| 00:00:02 |KEY(SQ)|KEY(SQ)|

| 12 | BITMAP CONVERSION TO ROWIDS | | | | | | | |

| 13 | BITMAP AND | | | | | | | |

| 14 | BITMAP MERGE | | | | | | | |

| 15 | BITMAP KEY ITERATION | | | | | | | |

| 16 | BUFFER SORT | | | | | | | |

|* 17 | TABLE ACCESS FULL | CHANNELS | 2 | 26 | 3 (0)| 00:00:01 | | |

|* 18 | BITMAP INDEX RANGE SCAN | CHANNEL_IND | | | | |KEY(SQ)|KEY(SQ)|

| 19 | BITMAP MERGE | | | | | | | |

| 20 | BITMAP KEY ITERATION | | | | | | | |

| 21 | BUFFER SORT | | | | | | | |

|* 22 | TABLE ACCESS FULL | TIMES | 183 | 2928 | 18 (0)| 00:00:01 | | |

|* 23 | BITMAP INDEX RANGE SCAN | TIME_IND | | | | |KEY(SQ)|KEY(SQ)|

| 24 | BITMAP MERGE | | | | | | | |

| 25 | BITMAP KEY ITERATION | | | | | | | |

| 26 | BUFFER SORT | | | | | | | |

| 27 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660D_1744F2 | 383 | 1915 | 2 (0)| 00:00:01 | | |

|* 28 | BITMAP INDEX RANGE SCAN | CUST_IND | | | | |KEY(SQ)|KEY(SQ)|

| 29 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660D_1744F2 | 383 | 5745 | 2 (0)| 00:00:01 | | |

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



Predicate Information (identified by operation id):

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



3 - filter("C"."CUST_STATE_PROVINCE"='CA')

5 - access("S"."TIME_ID"="T"."TIME_ID")

6 - filter("T"."CALENDAR_QUARTER_DESC"='1999-Q1' OR "T"."CALENDAR_QUARTER_DESC"='1999-Q2')

7 - access("S"."CUST_ID"="C0")

8 - access("S"."CHANNEL_ID"="CH"."CHANNEL_ID")

9 - filter("CH"."CHANNEL_DESC"='Catalog' OR "CH"."CHANNEL_DESC"='Internet')

17 - filter("CH"."CHANNEL_DESC"='Catalog' OR "CH"."CHANNEL_DESC"='Internet')

18 - access("S"."CHANNEL_ID"="CH"."CHANNEL_ID")

22 - filter("T"."CALENDAR_QUARTER_DESC"='1999-Q1' OR "T"."CALENDAR_QUARTER_DESC"='1999-Q2')

23 - access("S"."TIME_ID"="T"."TIME_ID")

28 - access("S"."CUST_ID"="C0")



Note

-----

- star transformation used for this statement





Statistics

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

9 recursive calls

16 db block gets

1526 consistent gets

0 physical reads

820 redo size

497 bytes sent via SQL*Net to client

408 bytes received via SQL*Net from client

1 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

0 rows processed



SH@PROD1>

 

三、查詢轉換(Query Transformation):

查詢轉換又稱爲查詢改寫,Oracle在硬解析的時候解析產生執行計劃之前需要對SQL文本進行等價改寫,以提高SQL的執行效率。在Oracle 10g之前查詢轉換是獨立於優化器之外的,即每條SQL都會進行查詢轉換,Oracle認爲等價改寫之後SQL執行效率更高,但有些時候並不是這樣的。故在Oracle10g之後查詢轉換和CBO是密切相關的,只對某些類型的查詢轉換(子查詢展開,複雜視圖合併等)計算成本,即Oracle會分別計算經過查詢轉換後的等價改寫SQL的成本和原始SQL的成本,只有當等價改寫SQL的成本值小於未經查詢轉換的原始SQL的成本時,Oracle纔會對目標SQL執行這些查詢轉換。

1.子查詢展開(Subqury Unnesting)

子查詢展開是指優化器不再將目標SQL中的子查詢單過一個獨立的獨立單元來獨立執行,而是將子查詢轉換爲它自身和外部查詢之間等價的表連接。子查詢展開通常情況下都會提高原SQL的執行效率,若不做子查詢展開,外部查詢的每一個結果集作爲一個單元傳給子查詢運行,並且會走FILTER類型的執行計劃。

子查詢展開類型:

①將子查詢拆開,即將子查詢中的表、視圖從子查詢中拿出來和外部查詢中的表、視圖進行表連接。Oracle10g以後經過子查詢展開後的等價改寫SQL的成本無論是否小於原來SQL的成本值。Oracle都會做子查詢展開查詢轉換。

②將子查詢轉換成一個內嵌視圖(Inline View),然後和外部查詢中的表、視圖進行表連接。Oracle10g以後經過子查詢展開後的等價改寫SQL的成本小於原來SQL的成本值,Oracle纔會做查詢轉換。

子查詢展開條件:

①SQL文本中where條件滿足以下條件:

single-row(=、<、>、<=、>=和<>)

EXISTS

NOT EXISTS

IN

NOT IN

ANY

ALL

②查詢轉換一定是等價的,對於不拆開子查詢會把它轉換成一個內嵌視圖來和外部查詢中的表、視圖進行連接,這種情況下一定是經過子查詢展開後的等價改寫SQL的成本小於原來SQL的成本值,Oracle纔會做子查詢展開。

 

例:在Oracle 10g以後之後將子查詢轉換成內嵌試圖的成本小於原來SQL成本時纔會進行做子查詢展開方式的查詢轉換,明顯一下示例SQL子查詢展開方式的SQL成本和consistent gets小於做內嵌視圖方式的查詢轉換。

子查詢展開:

SQL> select a.*

2 from scott.dept a

3 where a.deptno in (select b.deptno from scott.emp b

4 where b.empno >'7500' );

實際上以上SQL進行查詢轉換後將原SQL等價轉換成半連接方式:

select a.*

from scott.dept a,scott,emp b

where a.deptno semi=b.deptno

and b.empno >'7500'



Execution Plan

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

Plan hash value: 287487506



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

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

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

| 0 | SELECT STATEMENT | | 3 | 81 | 5 (20)| 00:00:01 |

| 1 | MERGE JOIN SEMI | | 3 | 81 | 5 (20)| 00:00:01 |

| 2 | TABLE ACCESS BY INDEX ROWID | DEPT | 4 | 80 | 2 (0)| 00:00:01 |

| 3 | INDEX FULL SCAN | PK_DEPT | 4 | | 1 (0)| 00:00:01 |

|* 4 | SORT UNIQUE | | 11 | 77 | 3 (34)| 00:00:01 |

| 5 | TABLE ACCESS BY INDEX ROWID| EMP | 11 | 77 | 2 (0)| 00:00:01 |

|* 6 | INDEX RANGE SCAN | PK_EMP | 11 | | 1 (0)| 00:00:01 |

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



Predicate Information (identified by operation id):

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



4 - access("A"."DEPTNO"="B"."DEPTNO")

filter("A"."DEPTNO"="B"."DEPTNO")

6 - access("B"."EMPNO">7500)





Statistics

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

0 recursive calls

0 db block gets

6 consistent gets

0 physical reads

0 redo size

768 bytes sent via SQL*Net to client

524 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

1 sorts (memory)

0 sorts (disk)

3 rows processed

 

no_unnest是不對該SQL做子查詢展開,即使用第二種方式將子查詢轉換成內嵌試圖的方式進行查詢轉換。

SQL> select a.*

2 from scott.dept a

3 where a.deptno in (select /*+ no_unnest */ b.deptno from scott.emp b

4 where b.empno >'7500' );





Execution Plan

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

Plan hash value: 3633697989



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

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

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

| 0 | SELECT STATEMENT | | 1 | 20 | 7 (0)| 00:00:01 |

|* 1 | FILTER | | | | | |

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

|* 3 | TABLE ACCESS BY INDEX ROWID| EMP | 2 | 14 | 2 (0)| 00:00:01 |

|* 4 | INDEX RANGE SCAN | PK_EMP | 11 | | 1 (0)| 00:00:01 |

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



Predicate Information (identified by operation id):

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



1 - filter( EXISTS (SELECT /*+ NO_UNNEST */ 0 FROM "SCOTT"."EMP" "B" WHERE

"B"."EMPNO">7500 AND "B"."DEPTNO"=:B1))

3 - filter("B"."DEPTNO"=:B1)

4 - access("B"."EMPNO">7500)





Statistics

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

1 recursive calls

0 db block gets

15 consistent gets

0 physical reads

0 redo size

768 bytes sent via SQL*Net to client

524 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

3 rows processed

2.視圖合併(View Mergging)

視圖合併是指Oracle不在將視圖中的作爲一個單元,而是將視圖內部中的SQL基表和外部查詢中的表做連接。

(1)簡單視圖合併(Simple View Merging)

簡單視圖合併是指:不包含外連接,以及所帶視圖的定義SQL中不包含distinct、group by等聚集函數的視圖合併。

簡單視圖合併Oracle會始終進行視圖合併,而不管經過視圖合併後的等價改寫SQL的成本值時候小於原SQL的成本值。對於簡單視圖合併是將視圖拆開會出現很多種情況(三表關聯會出現3*2=6的可能性)

例:通過以下執行計劃發現,Oracle已經默認做了簡單視圖合併。

SQL> select p.*

2 from test.v_1 v,test.product p

3 where p.object_name=v.table_name

4 and p.OBJECT_ID<'7000';



13 rows selected.





Execution Plan

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

Plan hash value: 432321455



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

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

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

| 0 | SELECT STATEMENT | | 178 | 27590 | 199 (0)| 00:00:03 |

|* 1 | HASH JOIN | | 178 | 27590 | 199 (0)| 00:00:03 |

|* 2 | HASH JOIN | | 109 | 14388 | 151 (0)| 00:00:02 |

|* 3 | TABLE ACCESS FULL | SALES | 106 | 3604 | 31 (0)| 00:00:01 |

| 4 | TABLE ACCESS BY INDEX ROWID| PRODUCT | 6738 | 644K| 120 (0)| 00:00:02 |

|* 5 | INDEX RANGE SCAN | IND_A | 6738 | | 16 (0)| 00:00:01 |

|* 6 | TABLE ACCESS FULL | EMP | 3752 | 86296 | 48 (0)| 00:00:01 |

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



Predicate Information (identified by operation id):

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



1 - access("A"."TABLE_NAME"="B"."TABLE_NAME")

2 - access("P"."OBJECT_NAME"="A"."TABLE_NAME")

3 - filter("A"."OWNER"='SYSTEM' AND "A"."TABLESPACE_NAME"='SYSAUX')

5 - access("P"."OBJECT_ID"<7000)

6 - filter("B"."LEAF_BLOCKS" IS NOT NULL)





Statistics

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

8 recursive calls

0 db block gets

380 consistent gets

0 physical reads

0 redo size

2098 bytes sent via SQL*Net to client

523 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

13 rows processed

我們可以通過hint no_merge 來讓Oracle優化器不進行視圖合併,執行計劃中出現如下view後邊name是對象名稱,說明沒有進行簡單視圖合併,執行計劃如下:

SQL> select /*+ no_merge(v)*/ p.*

2 from test.v_1 v,test.product p

3 where p.object_name=v.table_name

4 and p.OBJECT_ID<'7000';



13 rows selected.





Execution Plan

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

Plan hash value: 1310787303



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

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

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

| 0 | SELECT STATEMENT | | 178 | 20470 | 199 (0)| 00:00:03 |

|* 1 | HASH JOIN | | 178 | 20470 | 199 (0)| 00:00:03 |

| 2 | VIEW | V_1 | 173 | 2941 | 79 (0)| 00:00:01 |

|* 3 | HASH JOIN | | 173 | 9861 | 79 (0)| 00:00:01 |

|* 4 | TABLE ACCESS FULL | SALES | 106 | 3604 | 31 (0)| 00:00:01 |

|* 5 | TABLE ACCESS FULL | EMP | 3752 | 86296 | 48 (0)| 00:00:01 |

| 6 | TABLE ACCESS BY INDEX ROWID| PRODUCT | 6738 | 644K| 120 (0)| 00:00:02 |

|* 7 | INDEX RANGE SCAN | IND_A | 6738 | | 16 (0)| 00:00:01 |

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



Predicate Information (identified by operation id):

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



1 - access("P"."OBJECT_NAME"="V"."TABLE_NAME")

3 - access("A"."TABLE_NAME"="B"."TABLE_NAME")

4 - filter("A"."OWNER"='SYSTEM' AND "A"."TABLESPACE_NAME"='SYSAUX')

5 - filter("B"."LEAF_BLOCKS" IS NOT NULL)

7 - access("P"."OBJECT_ID"<7000)





Statistics

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

8 recursive calls

0 db block gets

381 consistent gets

0 physical reads

0 redo size

2106 bytes sent via SQL*Net to client

523 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

13 rows processed

(2)外連接視圖合併(Outer Join View Merging)

外連接視圖合併是指:針對那些使用了外連接,以及所帶視圖的定義SQL中不包含distinct、group by等聚合函數的目標申請的視圖合併。

外連接對視圖合併有很多限制,很多情況下內連接可以做的視圖合併,轉化成外連接之後就不會做視圖合併,原因是在保證語義相同的情況下不能做視圖轉換。所以,只有在該視圖作爲外連接的驅動表或者他作爲外連接的被驅動表但是視圖定義SQL語句中只包含一個表時該視圖和外部查詢做外連接纔可以做視圖合併

例:當外連接視圖作爲驅動表時Oracle會直接進行視圖合併:

SQL> select p.*

2 from test.v_1 v,test.product p

3 where p.object_name(+)=v.table_name

4 and p.OBJECT_ID<'7000';



13 rows selected.





Execution Plan

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

Plan hash value: 1397898977



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

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

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

| 0 | SELECT STATEMENT | | 178 | 27590 | 180 (0)| 00:00:03 |

|* 1 | HASH JOIN | | 178 | 27590 | 180 (0)| 00:00:03 |

|* 2 | HASH JOIN | | 109 | 14388 | 132 (0)| 00:00:02 |

|* 3 | TABLE ACCESS BY INDEX ROWID| SALES | 106 | 3604 | 12 (0)| 00:00:01 |

|* 4 | INDEX RANGE SCAN | OIND_C | 167 | | 1 (0)| 00:00:01 |

| 5 | TABLE ACCESS BY INDEX ROWID| PRODUCT | 6738 | 644K| 120 (0)| 00:00:02 |

|* 6 | INDEX RANGE SCAN | IND_A | 6738 | | 16 (0)| 00:00:01 |

|* 7 | TABLE ACCESS FULL | EMP | 3752 | 86296 | 48 (0)| 00:00:01 |

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



Predicate Information (identified by operation id):

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



1 - access("A"."TABLE_NAME"="B"."TABLE_NAME")

2 - access("P"."OBJECT_NAME"="A"."TABLE_NAME")

3 - filter("A"."TABLESPACE_NAME"='SYSAUX')

4 - access("A"."OWNER"='SYSTEM')

6 - access("P"."OBJECT_ID"<7000)

7 - filter("B"."LEAF_BLOCKS" IS NOT NULL)





Statistics

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

8 recursive calls

0 db block gets

303 consistent gets

0 physical reads

0 redo size

2098 bytes sent via SQL*Net to client

523 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

13 rows processed

當把外連接視圖做驅動表時,由於該視圖是兩個表連接(sales和emp)的視圖,所以Oracle不會進行視圖合併

SQL> select p.*

2 from test.v_1 v,test.product p

3 where p.object_name=v.table_name(+)

4 and p.OBJECT_ID<'7000';



6888 rows selected.





Execution Plan

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

Plan hash value: 970244956



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

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

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

| 0 | SELECT STATEMENT | | 6738 | 756K| 180 (0)| 00:00:03 |

|* 1 | HASH JOIN RIGHT OUTER | | 6738 | 756K| 180 (0)| 00:00:03 |

| 2 | VIEW | V_1 | 173 | 2941 | 60 (0)| 00:00:01 |

|* 3 | HASH JOIN | | 173 | 9861 | 60 (0)| 00:00:01 |

|* 4 | TABLE ACCESS BY INDEX ROWID| SALES | 106 | 3604 | 12 (0)| 00:00:01 |

|* 5 | INDEX RANGE SCAN | OIND_C | 167 | | 1 (0)| 00:00:01 |

|* 6 | TABLE ACCESS FULL | EMP | 3752 | 86296 | 48 (0)| 00:00:01 |

| 7 | TABLE ACCESS BY INDEX ROWID | PRODUCT | 6738 | 644K| 120 (0)| 00:00:02 |

|* 8 | INDEX RANGE SCAN | IND_A | 6738 | | 16 (0)| 00:00:01 |

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



Predicate Information (identified by operation id):

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



1 - access("P"."OBJECT_NAME"="V"."TABLE_NAME"(+))

3 - access("A"."TABLE_NAME"="B"."TABLE_NAME")

4 - filter("A"."TABLESPACE_NAME"='SYSAUX')

5 - access("A"."OWNER"='SYSTEM')

6 - filter("B"."LEAF_BLOCKS" IS NOT NULL)

8 - access("P"."OBJECT_ID"<7000)





Statistics

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

8 recursive calls

0 db block gets

1217 consistent gets

0 physical reads

0 redo size

384837 bytes sent via SQL*Net to client

5572 bytes received via SQL*Net from client

461 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

6888 rows processed

 

(3)複雜視圖合併(Compx View Merging)

複雜視圖合併是指:包含外連接,以及視圖定義的SQL包含distinct、group by等聚合函數的視圖合併。

複雜視圖合併和前兩者合併都不太一樣,由於包含了distinct、group by等聚合函數,而在執行語句時需要把視圖中的基表和外部關聯表進行關聯,那就意味着聚合函數需要放在最後執行,所以這種情況下並不一定帶來SQL語句執行效率的提升。如果distinct、group by等聚合函數能過濾掉大部分結果集,那麼顯然是先執行視圖在關聯外表的執行效率是最優化的。故在Oracle 10g以後對於複雜視圖合併的前提是:只有當經過複雜視圖合併後的等價改寫SQL的成本值小於原SQL的成本時,Oracle纔會對目標SQL執行復雜視圖合併。

例:由於視圖合併之後執行成本和合並前一樣而且邏輯讀更高,所以Oracle優化器沒有對其進行視圖合併。

SQL> select p.*

2 from test.v_2 v,test.product p

3 where p.object_name=v.table_name

4 and p.OBJECT_ID<'7000';



13 rows selected.





Execution Plan

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

Plan hash value: 593319594



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

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

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

| 0 | SELECT STATEMENT | | 178 | 31684 | 181 (1)| 00:00:03 |

| 1 | HASH GROUP BY | | 178 | 31684 | 181 (1)| 00:00:03 |

|* 2 | HASH JOIN | | 178 | 31684 | 180 (0)| 00:00:03 |

|* 3 | HASH JOIN | | 109 | 14388 | 132 (0)| 00:00:02 |

|* 4 | TABLE ACCESS BY INDEX ROWID| SALES | 106 | 3604 | 12 (0)| 00:00:01 |

|* 5 | INDEX RANGE SCAN | OIND_C | 167 | | 1 (0)| 00:00:01 |

| 6 | TABLE ACCESS BY INDEX ROWID| PRODUCT | 6738 | 644K| 120 (0)| 00:00:02 |

|* 7 | INDEX RANGE SCAN | IND_A | 6738 | | 16 (0)| 00:00:01 |

|* 8 | TABLE ACCESS FULL | EMP | 3752 | 168K| 48 (0)| 00:00:01 |

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



Predicate Information (identified by operation id):

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



2 - access("A"."TABLE_NAME"="B"."TABLE_NAME")

3 - access("P"."OBJECT_NAME"="A"."TABLE_NAME")

4 - filter("A"."TABLESPACE_NAME"='SYSAUX')

5 - access("A"."OWNER"='SYSTEM')

7 - access("P"."OBJECT_ID"<7000)

8 - filter("B"."LEAF_BLOCKS" IS NOT NULL)





Statistics

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

0 recursive calls

0 db block gets

300 consistent gets

0 physical reads

0 redo size

2172 bytes sent via SQL*Net to client

523 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

13 rows processed



SQL>

SQL> select /*+ no_merge(v)*/ p.*

2 from test.v_2 v,test.product p

3 where p.object_name(+)=v.table_name

4 and p.OBJECT_ID<'7000';



13 rows selected.





Execution Plan

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

Plan hash value: 3289631185



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

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

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

| 0 | SELECT STATEMENT | | 178 | 20826 | 181 (1)| 00:00:03 |

|* 1 | HASH JOIN | | 178 | 20826 | 181 (1)| 00:00:03 |

| 2 | VIEW | V_2 | 173 | 3287 | 61 (2)| 00:00:01 |

| 3 | HASH GROUP BY | | 173 | 14878 | 61 (2)| 00:00:01 |

|* 4 | HASH JOIN | | 173 | 14878 | 60 (0)| 00:00:01 |

|* 5 | TABLE ACCESS BY INDEX ROWID| SALES | 106 | 4240 | 12 (0)| 00:00:01 |

|* 6 | INDEX RANGE SCAN | OIND_C | 167 | | 1 (0)| 00:00:01 |

|* 7 | TABLE ACCESS FULL | EMP | 3752 | 168K| 48 (0)| 00:00:01 |

| 8 | TABLE ACCESS BY INDEX ROWID | PRODUCT | 6738 | 644K| 120 (0)| 00:00:02 |

|* 9 | INDEX RANGE SCAN | IND_A | 6738 | | 16 (0)| 00:00:01 |

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



Predicate Information (identified by operation id):

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



1 - access("P"."OBJECT_NAME"="V"."TABLE_NAME")

4 - access("A"."TABLE_NAME"="B"."TABLE_NAME")

5 - filter("A"."TABLESPACE_NAME"='SYSAUX')

6 - access("A"."OWNER"='SYSTEM')

7 - filter("B"."LEAF_BLOCKS" IS NOT NULL)

9 - access("P"."OBJECT_ID"<7000)





Statistics

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

8 recursive calls

0 db block gets

304 consistent gets

0 physical reads

0 redo size

2106 bytes sent via SQL*Net to client

523 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

13 rows processed

3.星型轉換(Star Transformation)

星型轉換是Oracle 優化器處理表連接的另一種優化手段,他發生在星型查詢中,並且存在位圖索引。它是將原星型連接中針對各個維度表(Dimension Table)的限制條件通過等價改寫的方式以額外的子查詢施加到事實表(Fact Table)上,通過事實表中的位圖索引對事實表進行索引掃描,進而提升效率。

SH@PROD1> set linesize 200;

SH@PROD1> set pagesize 200;

SH@PROD1> SELECT CH.CHANNEL_CLASS,C.CUST_CITY , T.CALENDAR_QUARTER_DESC,

2 SUM(S.AMOUNT_SOLD) SALES_AMOUNT

3 FROM SH.SALES S ,SH.TIMES T,SH.CUSTOMERS C,SH.CHANNELS CH

4 WHERE S.TIME_ID = T.TIME_ID

5 AND S.CUST_ID=C.CUST_ID

6 AND S.CHANNEL_ID = CH.CHANNEL_ID

7 AND C.CUST_STATE_PROVINCE ='CA'

8 AND CH.CHANNEL_DESC IN ('Internet','Catalog')

9 AND T.CALENDAR_QUARTER_DESC IN ('1999-Q1','1999-Q2')

10 GROUP BY CH.CHANNEL_CLASS,C.CUST_CITY , T.CALENDAR_QUARTER_DESC;



no rows selected





Execution Plan

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

Plan hash value: 4231244587



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

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

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

| 0 | SELECT STATEMENT | | 254 | 18542 | 557 (1)| 00:00:07 | | |

| 1 | TEMP TABLE TRANSFORMATION | | | | | | | |

| 2 | LOAD AS SELECT | SYS_TEMP_0FD9D660D_1744F2 | | | | | | |

|* 3 | TABLE ACCESS FULL | CUSTOMERS | 383 | 9958 | 405 (1)| 00:00:05 | | |

| 4 | HASH GROUP BY | | 254 | 18542 | 152 (2)| 00:00:02 | | |

|* 5 | HASH JOIN | | 254 | 18542 | 128 (1)| 00:00:02 | | |

|* 6 | TABLE ACCESS FULL | TIMES | 183 | 2928 | 18 (0)| 00:00:01 | | |

|* 7 | HASH JOIN | | 254 | 14478 | 110 (1)| 00:00:02 | | |

|* 8 | HASH JOIN | | 254 | 10668 | 107 (0)| 00:00:02 | | |

|* 9 | TABLE ACCESS FULL | CHANNELS | 2 | 42 | 3 (0)| 00:00:01 | | |

| 10 | PARTITION RANGE SUBQUERY | | 254 | 5334 | 104 (0)| 00:00:02 |KEY(SQ)|KEY(SQ)|

| 11 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 254 | 5334 | 104 (0)| 00:00:02 |KEY(SQ)|KEY(SQ)|

| 12 | BITMAP CONVERSION TO ROWIDS | | | | | | | |

| 13 | BITMAP AND | | | | | | | |

| 14 | BITMAP MERGE | | | | | | | |

| 15 | BITMAP KEY ITERATION | | | | | | | |

| 16 | BUFFER SORT | | | | | | | |

|* 17 | TABLE ACCESS FULL | CHANNELS | 2 | 26 | 3 (0)| 00:00:01 | | |

|* 18 | BITMAP INDEX RANGE SCAN | CHANNEL_IND | | | | |KEY(SQ)|KEY(SQ)|

| 19 | BITMAP MERGE | | | | | | | |

| 20 | BITMAP KEY ITERATION | | | | | | | |

| 21 | BUFFER SORT | | | | | | | |

|* 22 | TABLE ACCESS FULL | TIMES | 183 | 2928 | 18 (0)| 00:00:01 | | |

|* 23 | BITMAP INDEX RANGE SCAN | TIME_IND | | | | |KEY(SQ)|KEY(SQ)|

| 24 | BITMAP MERGE | | | | | | | |

| 25 | BITMAP KEY ITERATION | | | | | | | |

| 26 | BUFFER SORT | | | | | | | |

| 27 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660D_1744F2 | 383 | 1915 | 2 (0)| 00:00:01 | | |

|* 28 | BITMAP INDEX RANGE SCAN | CUST_IND | | | | |KEY(SQ)|KEY(SQ)|

| 29 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660D_1744F2 | 383 | 5745 | 2 (0)| 00:00:01 | | |

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



Predicate Information (identified by operation id):

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



3 - filter("C"."CUST_STATE_PROVINCE"='CA')

5 - access("S"."TIME_ID"="T"."TIME_ID")

6 - filter("T"."CALENDAR_QUARTER_DESC"='1999-Q1' OR "T"."CALENDAR_QUARTER_DESC"='1999-Q2')

7 - access("S"."CUST_ID"="C0")

8 - access("S"."CHANNEL_ID"="CH"."CHANNEL_ID")

9 - filter("CH"."CHANNEL_DESC"='Catalog' OR "CH"."CHANNEL_DESC"='Internet')

17 - filter("CH"."CHANNEL_DESC"='Catalog' OR "CH"."CHANNEL_DESC"='Internet')

18 - access("S"."CHANNEL_ID"="CH"."CHANNEL_ID")

22 - filter("T"."CALENDAR_QUARTER_DESC"='1999-Q1' OR "T"."CALENDAR_QUARTER_DESC"='1999-Q2')

23 - access("S"."TIME_ID"="T"."TIME_ID")

28 - access("S"."CUST_ID"="C0")



Note

-----

- star transformation used for this statement





Statistics

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

9 recursive calls

16 db block gets

1526 consistent gets

0 physical reads

820 redo size

497 bytes sent via SQL*Net to client

408 bytes received via SQL*Net from client

1 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

0 rows processed



SH@PROD1>

4.連接謂詞推入(Join Predicate Pushdown)

連接謂詞推入是Oracle 優化器處理表連接的另一種優化手段,它是指Oracle優化器會把視圖中定義的SQL當成一個獨立的單元來單獨執行,這樣能夠使用視圖中基表的索引來提升執行效率,然後把處於視圖外部的查詢連接條件推入到Oracle視圖內部SQL中,進而與視圖形成一種嵌套循環。值得注意的是,不是所有的連接謂詞推入轉換都能提升SQL的執行效率,因爲外部查詢的每一條記錄都會與視圖中的SQL關聯一次,如果外部查詢所在的結果集cardinality較大的話,即使視圖基表使用索引,那帶來的成本也是相當高的。只有當連接謂詞推入後嵌套循環連接的等價改寫SQL的成本小於原SQL的成本時,Oracle纔會對目標SQL做連接謂詞推入。

Oracle支持做連接謂詞推入的視圖類型:

·視圖定義的SQL語句中包含UNion all/union的視圖。

·視圖定義的SQL語句中包含distinct的視圖。

·視圖定義的SQL語句中包含group by的視圖。

·視圖定義的SQL語句中包含外連接的視圖。

·視圖定義的SQL語句中包含反連接的視圖。

·視圖定義的SQL語句中包含半連接的視圖。

例:從以下執行計劃中可看出,Oracle優化器默認沒有對該SQL進行視圖合併,連接方式採用的是hash join。

SQL> select p.*

2 from test.v_4 v,test.product p

3 where p.object_name=v.table_name;

and p.OBJECT_ID<'7000';

7884 rows selected.





Execution Plan

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

Plan hash value: 1520040170



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

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

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

| 0 | SELECT STATEMENT | | 12610 | 1416K| 425 (1)| 00:00:06 |

|* 1 | HASH JOIN | | 12610 | 1416K| 425 (1)| 00:00:06 |

| 2 | VIEW | V_4 | 7537 | 125K| 81 (3)| 00:00:01 |

| 3 | SORT UNIQUE | | 7537 | 161K| 81 (3)| 00:00:01 |

| 4 | UNION-ALL | | | | | |

| 5 | TABLE ACCESS FULL| SALES | 2800 | 56000 | 31 (0)| 00:00:01 |

| 6 | TABLE ACCESS FULL| EMP | 4737 | 106K| 48 (0)| 00:00:01 |

| 7 | TABLE ACCESS FULL | PRODUCT | 86047 | 8234K| 343 (1)| 00:00:05 |

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



Predicate Information (identified by operation id):

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



1 - access("P"."OBJECT_NAME"="V"."TABLE_NAME")





Statistics

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

8 recursive calls

0 db block gets

2014 consistent gets

1230 physical reads

0 redo size

458324 bytes sent via SQL*Net to client

6298 bytes received via SQL*Net from client

527 SQL*Net roundtrips to/from client

1 sorts (memory)

0 sorts (disk)

7884 rows processed

 

接下來看一下使用視圖合併的效果,push_pred hint是對視圖中的SQL做連接謂詞接入合併,連接方式採用nested pool,成本劇增,故Oracle沒有做視圖合併。

SQL> select /*+ merge(v) push_pred(v) */ p.*

2 from test.v_4 v,test.product p

3 where p.object_name=v.table_name;

and p.OBJECT_ID<'7000';







7884 rows selected.





Execution Plan

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

Plan hash value: 3755235045



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

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

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

| 0 | SELECT STATEMENT | | 12610 | 1231K| 6979K (3)| 23:16:00 |

| 1 | NESTED LOOPS | | 12610 | 1231K| 6979K (3)| 23:16:00 |

| 2 | TABLE ACCESS FULL | PRODUCT | 86047 | 8234K| 343 (1)| 00:00:05 |

| 3 | VIEW | V_4 | 1 | 2 | 81 (3)| 00:00:01 |

| 4 | SORT UNIQUE | | 2 | 43 | 81 (3)| 00:00:01 |

| 5 | UNION ALL PUSHED PREDICATE | | | | | |

|* 6 | TABLE ACCESS FULL | SALES | 1 | 20 | 31 (0)| 00:00:01 |

|* 7 | TABLE ACCESS FULL | EMP | 1 | 23 | 48 (0)| 00:00:01 |

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



Predicate Information (identified by operation id):

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



6 - filter("A"."TABLE_NAME"="P"."OBJECT_NAME")

7 - filter("B"."INDEX_NAME"="P"."OBJECT_NAME")





Statistics

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

8 recursive calls

0 db block gets

22460020 consistent gets

1230 physical reads

0 redo size

458324 bytes sent via SQL*Net to client

6298 bytes received via SQL*Net from client

527 SQL*Net roundtrips to/from client

86047 sorts (memory)

0 sorts (disk)

7884 rows processed

 

5.連接因式分解(Join Factorization)

連接因式分解是優化器處理待UNION ALL的目標SQL的另一種優化手段(11.2.0.1 and later)。它是指對於UNION ALL的語句不在單獨執行,而是將公共部分拿出來作爲一個單獨的結果集,然後在和原UNION ALL中剩下的部分表連接。它能很明顯的提高SQL的執行效率。在11.2.0.1及以後的版本中即使視圖語句中包含UNION ALL不能對其進行視圖合併,也不會把視圖當成一個獨立單元來做連接謂詞推入,因爲他隨時都會在連接因式分解。

SQL> select p.*

2 from test.v_5 v,test.product p

3 where p.object_name=v.table_name;

and p.OBJECT_ID<'7000';

8277 rows selected.





Execution Plan

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

Plan hash value: 2561783368



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

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

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

| 0 | SELECT STATEMENT | | 17751 | 1993K| 1109 (1)| 00:00:14 |

|* 1 | HASH JOIN | | 17751 | 1993K| 1109 (1)| 00:00:14 |

| 2 | VIEW | V_5 | 10610 | 176K| 765 (1)| 00:00:10 |

| 3 | UNION-ALL | | | | | |

|* 4 | HASH JOIN | | 2685 | 141K| 374 (1)| 00:00:05 |

| 5 | TABLE ACCESS FULL| SALES | 2800 | 56000 | 31 (0)| 00:00:01 |

|* 6 | TABLE ACCESS FULL| PRODUCT | 2681 | 91154 | 343 (1)| 00:00:05 |

|* 7 | HASH JOIN | | 7925 | 371K| 391 (1)| 00:00:05 |

| 8 | TABLE ACCESS FULL| EMP | 4737 | 106K| 48 (0)| 00:00:01 |

| 9 | TABLE ACCESS FULL| PRODUCT | 86047 | 2100K| 343 (1)| 00:00:05 |

| 10 | TABLE ACCESS FULL | PRODUCT | 86047 | 8234K| 343 (1)| 00:00:05 |

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



Predicate Information (identified by operation id):

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



1 - access("P"."OBJECT_NAME"="V"."TABLE_NAME")

4 - access("A"."TABLE_NAME"="P"."OBJECT_NAME")

6 - filter("P"."OBJECT_TYPE"='TABLE')

7 - access("E"."INDEX_NAME"="P"."OBJECT_NAME")





Statistics

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

0 recursive calls

0 db block gets

4504 consistent gets

255 physical reads

0 redo size

465440 bytes sent via SQL*Net to client

6585 bytes received via SQL*Net from client

553 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

8277 rows processed

6.表擴展(Table Expansion)

表擴展是優化器針對處理分區表的SQL的一種優化手段(11.2.0.1 and later)。它是指當目標SQL中的某個局部索引變得不可用(index status is UNUSABLE)那麼oracle會將目標SQL改寫成安分區union ALL的形式,這樣除了不可用索引的分區以外其他分區還是可以正常使用分區索引的。他的優勢也是顯而易見的能夠大幅度提升SQL效率。

select * from sh.sales where

prod_id=30 and time_id

between to_date('2000-02-01','yyyy-mm-dd')

and to_date('2007-02-01','yyyy-mm-dd');



for Oracle 11gR2 before



SYS@PROD1> select * from sh.sales where

2 prod_id=30 and time_id

3 between to_date('2000-02-01','yyyy-mm-dd')

4 and to_date('2007-02-01','yyyy-mm-dd');



14848 rows selected.





Execution Plan

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

Plan hash value: 2385610757



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

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

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

| 0 | SELECT STATEMENT | | 14911 | 422K| 239 (1)| 00:00:03 | | |

| 1 | PARTITION RANGE ITERATOR | | 14911 | 422K| 239 (1)| 00:00:03 | 13 | 28 |

|* 2 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 14911 | 422K| 239 (1)| 00:00:03 | 13 | 28 |

|* 3 | INDEX RANGE SCAN | PROD_ID_IND | 16701 | | 80 (0)| 00:00:01 | 13 | 28 |

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



Predicate Information (identified by operation id):

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



2 - filter("TIME_ID">=TO_DATE(' 2000-02-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND

"TIME_ID"<=TO_DATE(' 2007-02-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))

3 - access("PROD_ID"=30)





Statistics

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

1 recursive calls

0 db block gets

2083 consistent gets

0 physical reads

0 redo size

533356 bytes sent via SQL*Net to client

11299 bytes received via SQL*Net from client

991 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

14848 rows processed

 

7.表移除(Table Elimination)for Oracle 10g later

表移除是優化器處理帶奪標連接的目標SQL的另一種優化手段(10.2.0.1 and alert)。它是指優化器會先把SQL中存在與否對執行結果都沒影響的表移除,減少表的連接次數。提升SQL效率

SYS@PROD1> select ename from scott.emp ,scott.dept where emp.deptno=dept.deptno;



14 rows selected.





Execution Plan

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

Plan hash value: 3956160932



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

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

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

| 0 | SELECT STATEMENT | | 14 | 126 | 3 (0)| 00:00:01 |

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

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



Predicate Information (identified by operation id):

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



1 - filter("EMP"."DEPTNO" IS NOT NULL)





Statistics

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

1 recursive calls

0 db block gets

8 consistent gets

0 physical reads

0 redo size

595 bytes sent via SQL*Net to client

420 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

14 rows processed

8.Oracle處理SQL中的IN

在Oracle數據庫中IN和OR是等價的,Oracle優化器會處理帶IN的SQL轉換爲帶OR的SQL,在Oracle數據庫裏,IN和OR是等價的,優化器在處理帶IN的SQL時實際上是將其轉換爲OR的等價SQL。

select * from emp

where deptno in (10,20)

等價轉換爲

select * from emp

where deptno=10 or deptno=20;

 

(1)IN - List Lterator

IN - List Lterator是Oracle針對IN後邊爲常量的一種處理方法,Oracle會遍歷SQL中IN後面的所有值然後跟SQL結果集去匹配,如果存在就返回結果集中的值,如果不存在就繼續遍歷下一個值。

注意:

①IN - List Lterator方式是IN後面接常量的首選處理方法,處理效率比 IN - List Expansion方式效率高。

②IN - List Lterator方式的前提是IN所在列上必須有索引。

③不能強制讓Oracle奏IN - List Lterator類型的執行計劃,Oracle沒有相關的HINT,但可以通過聯合攝製10142和10157事件來禁掉IN - List Lterator。


 

select * from sh.sales

where PROD_ID in (33,40,41);



SYS@PROD1> create index sh.prod_id_ind on sh.sales(prod_id) local;



Index created.



SYS@PROD1> exec dbms_stats.gather_table_stats('sh','sales');



PL/SQL procedure successfully completed.



SH@PROD1> select * from sh.sales

2 where PROD_ID in (33,40,41);



62311 rows selected.





Execution Plan

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

Plan hash value: 1710189240



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

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

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

| 0 | SELECT STATEMENT | | 61010 | 1727K| 462 (1)| 00:00:06 | | |

| 1 | PARTITION RANGE ALL | | 61010 | 1727K| 462 (1)| 00:00:06 | 1 | 28 |

| 2 | INLIST ITERATOR | | | | | | | |

| 3 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 61010 | 1727K| 462 (1)| 00:00:06 | 1 | 28 |

|* 4 | INDEX RANGE SCAN | PROD_ID_IND | 61010 | | 152 (0)| 00:00:02 | 1 | 28 |

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



Predicate Information (identified by operation id):

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



4 - access("PROD_ID"=33 OR "PROD_ID"=40 OR "PROD_ID"=41)





Statistics

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

1 recursive calls

0 db block gets

8768 consistent gets

0 physical reads

0 redo size

2232134 bytes sent via SQL*Net to client

46113 bytes received via SQL*Net from client

4156 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

62311 rows processed



SH@PROD1>



SYS@PROD1> drop index sh.prod_id_ind;



Index dropped.



SH@PROD1> select * from sh.sales

2 where PROD_ID in (33,40,41);



62311 rows selected.





Execution Plan

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

Plan hash value: 1550251865



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

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

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

| 0 | SELECT STATEMENT | | 38285 | 1084K| 490 (2)| 00:00:06 | | |

| 1 | PARTITION RANGE ALL| | 38285 | 1084K| 490 (2)| 00:00:06 | 1 | 28 |

|* 2 | TABLE ACCESS FULL | SALES | 38285 | 1084K| 490 (2)| 00:00:06 | 1 | 28 |

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



Predicate Information (identified by operation id):

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



2 - filter("PROD_ID"=33 OR "PROD_ID"=40 OR "PROD_ID"=41)





Statistics

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

1 recursive calls

0 db block gets

5869 consistent gets

0 physical reads

0 redo size

2232186 bytes sent via SQL*Net to client

46113 bytes received via SQL*Net from client

4156 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

62311 rows processed



SH@PROD1>

 

(2)IN - List EXpansion / OR Expansion

IN - list Expansion 是Oracle針對IN後面是常量的另外一種處理方法,實際上是優化器將IN後邊的常量拆開然後把每個常量提出來形成一個分支,然後union ALL連接,實際上是吧SQL改寫成union all的連接形式。

優點:

IN - list Expansion改寫成union all分支以後各個分支可以各自走索引、分區修剪、表連接等相關的執行計劃而不互相干擾。

缺點:

在未做IN - list Expansion改寫前只需生成一個執行計劃,IN - list Expansion改寫之後會對每個分支生成一個執行計劃,執行計劃會隨着IN後面的常量數增加而增加,一段IN後面的常量集合包含的元素數量非常多的時候,IN - list Expansion的解析時間非常長。

select /*+ use_concat*/ * from sh.sales

where PROD_ID in (33,40,41);

SYS@PROD1> select /*+ use_concat*/ * from sh.sales

2 where PROD_ID in (33,40,41);



62311 rows selected.





Execution Plan

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

Plan hash value: 1710189240



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

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

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

| 0 | SELECT STATEMENT | | 62035 | 1756K| 468 (1)| 00:00:06 | | |

| 1 | PARTITION RANGE ALL | | 62035 | 1756K| 468 (1)| 00:00:06 | 1 | 28 |

| 2 | INLIST ITERATOR | | | | | | | |

| 3 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 62035 | 1756K| 468 (1)| 00:00:06 | 1 | 28 |

|* 4 | INDEX RANGE SCAN | PROD_ID_IND | 62035 | | 152 (0)| 00:00:02 | 1 | 28 |

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



Predicate Information (identified by operation id):

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



4 - access("PROD_ID"=33 OR "PROD_ID"=40 OR "PROD_ID"=41)





Statistics

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

0 recursive calls

0 db block gets

8782 consistent gets

0 physical reads

0 redo size

2232106 bytes sent via SQL*Net to client

46114 bytes received via SQL*Net from client

4156 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

62311 rows processed



SYS@PROD1> alter session set events '10142 trace name context forever';



Session altered.



SYS@PROD1> alter session set events '10157 trace name context forever';



Session altered.



SYS@PROD1> exec dbms_stats.gather_table_stats('sh','sales',cascade=>true,no_invalidate=>false);



PL/SQL procedure successfully completed.



SYS@PROD1> set autotrace trace;

SYS@PROD1> select /*+ use_concat*/ * from sh.sales

2 where PROD_ID in (33,40,41);



62311 rows selected.





Execution Plan

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

Plan hash value: 2873107102



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

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

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

| 0 | SELECT STATEMENT | | 62035 | 1756K| 528 (1)| 00:00:07 | | |

| 1 | CONCATENATION | | | | | | | |

| 2 | PARTITION RANGE ALL | | 14974 | 424K| 135 (0)| 00:00:02 | 1 | 28 |

| 3 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 14974 | 424K| 135 (0)| 00:00:02 | 1 | 28 |

|* 4 | INDEX RANGE SCAN | PROD_ID_IND | 14974 | | 59 (0)| 00:00:01 | 1 | 28 |

| 5 | PARTITION RANGE ALL | | 22872 | 647K| 191 (0)| 00:00:03 | 1 | 28 |

| 6 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 22872 | 647K| 191 (0)| 00:00:03 | 1 | 28 |

|* 7 | INDEX RANGE SCAN | PROD_ID_IND | 22872 | | 75 (0)| 00:00:01 | 1 | 28 |

| 8 | PARTITION RANGE ALL | | 24189 | 685K| 201 (0)| 00:00:03 | 1 | 28 |

| 9 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 24189 | 685K| 201 (0)| 00:00:03 | 1 | 28 |

|* 10 | INDEX RANGE SCAN | PROD_ID_IND | 24189 | | 78 (0)| 00:00:01 | 1 | 28 |

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



Predicate Information (identified by operation id):

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



4 - access("PROD_ID"=41)

7 - access("PROD_ID"=33)

10 - access("PROD_ID"=40)





Statistics

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

1 recursive calls

0 db block gets

8782 consistent gets

0 physical reads

0 redo size

2232106 bytes sent via SQL*Net to client

46113 bytes received via SQL*Net from client

4156 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

62311 rows processed

 

(3)IN - List Filer

 

IN - List Filer 是針對IN後面是子查詢的另一種處理方法,優化器會把IN後面的子查詢所對應的結果集當做過濾條件,並且奏FILTER類型的執行計劃。

select * from sh.sales

where PROD_ID in (select PROD_ID from sh.CUSTOMERS where CUST_LAST_NAME='Ryan');

SYS@PROD1> select * from sh.sales

2 where PROD_ID in (select PROD_ID from sh.CUSTOMERS where CUST_LAST_NAME='Ryan');





918843 rows selected.





Execution Plan

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

Plan hash value: 1844385983



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

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

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

| 0 | SELECT STATEMENT | | 12762 | 361K| 1571 (1)| 00:00:19 | | |

|* 1 | FILTER | | | | | | | |

| 2 | PARTITION RANGE ALL| | 918K| 25M| 490 (2)| 00:00:06 | 1 | 28 |

| 3 | TABLE ACCESS FULL | SALES | 918K| 25M| 490 (2)| 00:00:06 | 1 | 28 |

|* 4 | FILTER | | | | | | | |

|* 5 | TABLE ACCESS FULL | CUSTOMERS | 2 | 16 | 15 (0)| 00:00:01 | | |

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



Predicate Information (identified by operation id):

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



1 - filter( EXISTS (SELECT 0 FROM "SH"."CUSTOMERS" "CUSTOMERS" WHERE :B1=:B2 AND

"CUST_LAST_NAME"='Ryan'))

4 - filter(:B1=:B2)

5 - filter("CUST_LAST_NAME"='Ryan')





Statistics

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

2 recursive calls

0 db block gets

63945 consistent gets

0 physical reads

0 redo size

33489026 bytes sent via SQL*Net to client

674236 bytes received via SQL*Net from client

61258 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

918843 rows processed

happy new year !

                                                                                                   — C.A.P.   2020 於北京

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章