Oracle中常見的Hint(一)

Oracle中的Hint能夠用來調整SQL的執行計劃,提升SQL執行效率。下面分類介紹Oracle數據庫中常見的Hint。這裏描述的是Oracle11gR2中的常見Hint,Oracle數據庫中各個版本中的Hint都不盡相同,因此這裏講述的的Hint可能並不適用於Oracle早期的版本。sql

1、與優化器模式相關的Hint數據庫

一、ALL_ROWS緩存

ALL_ROWS是針對整個目標SQL的Hint,它的含義是讓優化器啓用CBO,並且在獲得目標SQL的執行計劃時會選擇那些吞吐量最佳的執行路徑。這裏的「吞吐量最佳」是指資源消耗量(即對I/O、CPU等硬件資源的消耗量)最小,也就是說在ALL_ROWS Hint生效的狀況下,優化器會啓用CBO並且會依據各個執行路徑的資源消耗量來計算它們各自的成本。session

ALL_ROWS Hint的格式以下:oracle

/*+ ALL_ROWS */app

使用範例:ide

select /*+ all_rows */ empno,ename,sal,job
  from emp
 where empno=7396;

從Oracle10g開始,ALL_ROWS就是默認的優化器模式,啓用的就是CBO。函數

scott@TEST>show parameter optimizer_mode

NAME				     TYPE			       VALUE
------------------------------------ --------------------------------- ------------------------------
optimizer_mode			     string			       ALL_ROWS

若是目標SQL中除了ALL_ROWS以外還使用了其餘與執行路徑、錶鏈接相關的Hint,優化器會優先考慮ALL_ROWS。優化

二、FIRST_ROWS(n)spa

FIRST_ROWS(n)是針對整個目標SQL的Hint,它的含義是讓優化器啓用CBO模式,並且在獲得目標SQL的執行計劃時會選擇那些能以最快的響應時間返回頭n條記錄的執行路徑,也就是說在FIRST_ROWS(n) Hint生效的狀況下,優化器會啓用CBO,並且會依據返回頭n條記錄的響應時間來決定目標SQL的執行計劃。

FIRST_ROWS(n)格式以下:

/*+ FIRST_ROWS(n) */

使用範例

select /*+ first_rows(10) */ empno,ename,sal,job
  from emp
 where deptno=30;

上述SQL中使用了/*+ first_rows(10) */,其含義是告訴優化器咱們想以最短的響應時間返回知足條件"deptno=30"的前10條記錄。

注意,FIRST_ROWS(n) Hint和優化器模式FIRST_ROWS_n不是一一對應的。優化器模式FIRST_ROWS_n中的n只能是一、十、100、1000。但FIRST_ROWS(n) Hint中的n還能夠是其餘值。

scott@TEST>alter session set optimizer_mode=first_rows_9;
ERROR:
ORA-00096: invalid value FIRST_ROWS_9 for parameter optimizer_mode, must be from among first_rows_1000, first_rows_100, first_rows_10, first_rows_1, first_rows, all_rows, choose,rule

scott@TEST>set autotrace traceonly
scott@TEST>select /*+ first_rows(9) */ empno from emp;

14 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 179099197

---------------------------------------------------------------------------
| Id  | Operation	 | Name   | Rows  | Bytes | Cost (%CPU)| Time	  |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT |	  |	9 |    36 |	1   (0)| 00:00:01 |
|   1 |  INDEX FULL SCAN | PK_EMP |	9 |    36 |	1   (0)| 00:00:01 |
---------------------------------------------------------------------------

若是在UPDATE、DELETE或者含以下內容的查詢語句中使用了FIRST_ROWS(n) Hint,則該Hint會被忽略:

  • 集合運算(如UNION,INTERSACT,MINUS,UNION ALL等)

  • GROUP BY 

  • FOR UPDATE

  • 聚合函數(好比SUM等)

  • DISTINCT

  • ORDER BY(對應的排序列上沒有索引)

這裏優化器會忽略FIRST_ROWS(n) Hint是由於對於上述類型的SQL而言,Oracle必須訪問全部的行記錄後才能返回知足條件的頭n行記錄,即在上述狀況下,使用該Hint是沒有意義的。

三、RULE

RULE是針對整個目標SQL的Hint,它表示對目標SQL啓用RBO。

格式以下:

/*+ RULE */

使用範例:

select /*+ rule */ empno,ename,sal,job
  from emp
 where deptno=30;

RULE不能與除DRIVING_SITE之外的Hint聯用,當RULE與除DRIVING_SITE之外的Hint聯用時,其餘Hint可能會失效;當RULE與DRIVING_SITE聯用時,它自身可能會失效,因此RULE Hint最好是單獨使用。

通常狀況下,並不推薦使用RULE Hint。一來是由於Oracle早就不支持RBO了,二來啓用RBO後優化器在執行目標SQL時可選擇的執行路徑將大大減小,不少執行路徑RBO根本就不支持(好比哈希鏈接),就也就意味着啓用RBO後目標SQL跑出正確執行計劃的機率將大大下降。

由於不少執行路徑RBO根本就不支持,因此即便在目標SQL中使用了RULE Hint,若是出現了以下這些狀況(包括但不限於),RULE Hint依然會被Oracle忽略。

  • 目標SQL除RULE以外還聯合使用了其餘Hint(如DRIVING_SITE)。

  • 目標SQL使用了並行執行

  • 目標SQL所涉及的對象有IOT

  • 目標SQL所涉及的對象有分區表

......

2、與表訪問相關的Hint

一、FULL 

FULL是針對單個目標表的Hint,它的含義是讓優化器對目標表執行全表掃描。

格式以下:

/*+ FULL(目標表) */

使用範例:

select /*+ full(emp) */ empno,ename,sal,job
  from emp
 where deptno=30;

上述SQL中Hint的含義是讓優化器對目標表EMP執行全表掃描操做,而不考慮走表EMP上的任何索引(即便列EMPNO上有主鍵索引)。

二、ROIWD

ROIWD是針對單個目標表的Hint,它的含義是讓優化器對目標表執行RWOID掃描。只有目標SQL中使用了含ROWID的where條件時ROWID Hint纔有意義。

格式以下:

/*+ ROWID(目標表) */

使用範例:

select /*+ rowid(emp) */ empno,ename,sal,job
  from emp
 where rowid='AAAR3xAAEAAAACXAAA';

Oracle 11gR2中即便使用了ROWID Hint,Oracle仍是會將讀到的塊緩存在Buffer Cache中。

3、與索引訪問相關的Hint

一、INDEX

INDEX是針對單個目標表的Hint,它的含義是讓優化器對目標表的的目標索引執行索引掃描操做。

INDEX Hint中的目標索引幾乎能夠是Oracle數據庫中全部類型的索引(包括B樹索引、位圖索引、函數索引等)。

INDEX Hint的模式有四種:

格式1 /*+ INDEX(目標表 目標索引) */

格式2 /*+ INDEX(目標表 目標索引1 目標索引2 …… 目標索引n) */

格式3 /*+ INDEX(目標表 (目標索引1的索引列名) (目標索引2的索引列名) …… (目標索引n的索引列名)) */

格式4 /*+ INDEX(目標表) */

格式1表示僅指定了目標表上的一個目標索引,此時優化器只會考慮對這個目標索引執行索引掃描操做,而不會去考慮全表掃描或者對該目標表上的其餘索引執行索引掃描操做。

格式2表示指定了目標表上的n個目標索引,此時優化器只會考慮對這n個目標索引執行索引掃描操做,而不會去考慮全表掃描或者對該目標表上的其餘索引執行索引掃描操做。注意,優化器在考慮這n個目標索引時,多是分別計算出單獨掃描各個目標索引的成本後,再選擇其中成本值最低的索引;也多是先分別掃描目標索引中的兩個或多個索引,而後再對掃描結果執行合併操做。固然,後面這種可能性的前提條件是優化器計算出來這樣作的成本值是最低的。

格式三也是表是指定了目標表上的n個目標索引,只不過此時是用指定目標索引的索引列名來代替對應的目標索引名。若是目標索引是複合索引,則在用於指定該索引列名的括號內也能夠指定該目標索引的多個索引列,各個索引列之間用空格分隔就能夠了。

格式的表示指定了目標表上全部已存在的索引,此時優化器只會考慮對該目標表上全部已存在的索引執行索引掃描操做,而不會去考慮全表掃描操做。注意,這裏優化器在考慮該目標表上全部已存在的索引時,多是分別計算出單獨掃描這些索引的成本後再選擇其中成本值最低的索引;也多是先分別掃描這些索引中的兩個或多個索引,而後再對掃描結果執行合併操做。固然,後面這種可能性的前提條件是優化器計算出來這樣作的成本值是最低的。

使用範例:

select /*+ index(emp pk_emp) */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;
 
select /*+ index(emp pk_emp idx_emp_mgr idx_emp_dept) */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;
 
select /*+ index(emp (empno) (mgr) (deptno)) */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;
 
select /*+ index */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;

二、NO_INDEX

NO_INDEX是針對單個目標表的Hint,它是INDEX的反義Hint,其含義是讓優化器不對目標表上的目標索引執行掃描操做。

INDEX Hint中的目標索引也幾乎能夠是Oracle數據庫中全部類型的索引(包括B樹索引、位圖索引、函數索引等)。

格式有以下三種:

格式1 /*+ NO_INDEX(目標表 目標索引) */

格式2 /*+ NO_INDEX(目標表 目標索引1 目標索引2 …… 目標索引n) */

格式3 /*+ NO_INDEX(目標表) */

格式1表示僅指定了目標表上的一個目標索引,此時優化器只是不會考慮對這個目標索引執行索引掃描操做,但仍是會考慮全表掃描或者對該目標表上的其餘索引執行索引掃描操做。

格式2表示指定了目標表上的n個目標索引,此時優化器只是不會考慮對這n個目標索引執行索引掃描操做,但仍是會考慮全表掃描或者對該目標表上的其餘索引執行索引掃描操做。

格式3表示指定了目標表上的全部已存在的索引,即此時優化器不會考慮對該目標表上全部已存在的索引執行索引掃描操做,這至關於對目標表指定了全表掃描。

使用範例:

select /*+ no_index(emp pk_emp) */ empno,ename,sal,job
  from emp
where empno=7369 and mgr=7902 and deptno=20;
 
select /*+ no_index(emp pk_emp idx_emp_mgr idx_emp_dept) */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;
 
select /*+ no_index */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;

三、INDEX_DESC

INDEX_DESC是針對單個目標表的Hint,它的含義是讓優化器對目標表上的目標索引執行索引降序掃描操做。若是目標索引是升序的,則INDEX_DESC Hint會使Oracle以降序的方式掃描該索引;若是目標索引是降序的,則INDEX_DESC Hint會使Oracle以升序的方式掃描該索引。

格式有三種:

格式1 /*+ INDEX_DESC(目標表 目標索引) */

格式2 /*+ INDEX_DESC(目標表 目標索引1 目標索引2 …… 目標索引n) */

格式3 /*+ INDEX_DESC(目標表) */

上述3種格式的含義和INDEX中對應格式的含義相同。

使用範例:

select /*+ index_desc(emp pk_emp) */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;
 
select /*+ index_desc(emp pk_emp idx_emp_mgr idx_emp_dept) */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;
 
select /*+ index_desc */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;

實例:

scott@TEST>select /*+ index_desc(emp,pk_emp) */ empno from emp;

     EMPNO
----------
      7934
      7902
      7900
      7876
      7844
      7839
      7788
      7782
      7698
      7654
      7566
      7521
      7499
      7369

14 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 1838043032

-------------------------------------------------------------------------------------
| Id  | Operation		   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT	   |	    |	 10 |	 40 |	  1   (0)| 00:00:01 |
|   1 |  INDEX FULL SCAN DESCENDING| PK_EMP |	 10 |	 40 |	  1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------
......

四、INDEX_COMBINE

INDEX_COMBINE是針對單個目標表的Hint,它的含義是讓優化器對目標表上的多個目標索引執行位圖布爾運算。Oracle數據庫裏有一個映射函數(Mapping Function),它能夠實例B*Tree索引中的ROWID和對應位圖索引中的位圖之間的互相轉換,因此INDEX_COMBINE Hint並不侷限於位圖索引,它的做用對象也能夠是B*Tree索引。

格式有以下兩種

格式1 /*+ INDEX_COMBINE(目標表 目標索引1 目標索引2 …… 目標索引n) */

格式2 /*+ INDEX_COMBINE(目標表) */

格式1表示指定了目標表上的n個目標索引,此時優化器會考慮對這n個目標索引中的兩個或多個執行位圖布爾運算。

格式2表示指定了目標表上全部已存在的索引,此時優化器會考慮對該表上已存在的全部索引中的兩個或多個執行位圖布爾運算。

使用範例:

select /*+ index_combine(emp pk_emp idx_emp_mgr) */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 ;
 
select /*+ index_combine(emp pk_emp idx_emp_mgr idx_emp_deptno) */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;
 
select /*+ index_combine(emp) */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;

下面看一個實例,在表EMP上建立兩個索引

scott@TEST>create index idx_emp_mgr on emp(mgr);

Index created.

scott@TEST>create index idx_emp_dept on emp(deptno);

Index created.

scott@TEST>select /*+ index_combine(emp pk_emp idx_emp_mgr idx_emp_deptno) */ empno,ename,sal,job
  2   from emp
  3   where empno=7369 and mgr=7902 and deptno=20;

     EMPNO ENAME				 SAL JOB
---------- ------------------------------ ---------- ---------------------------
      7369 SMITH				 800 CLERK


Execution Plan
----------------------------------------------------------
Plan hash value: 1816402415

-------------------------------------------------------------------------------------------------
| Id  | Operation			 | Name 	| Rows	| Bytes | Cost (%CPU)| Time	|
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT		 |		|     1 |    29 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID	 | EMP		|     1 |    29 |     2   (0)| 00:00:01 |
|   2 |   BITMAP CONVERSION TO ROWIDS	 |		|	|	|	     |		|
|   3 |    BITMAP AND			 |		|	|	|	     |		|
|   4 |     BITMAP CONVERSION FROM ROWIDS|		|	|	|	     |		|
|*  5 |      INDEX RANGE SCAN		 | PK_EMP	|	|	|     0   (0)| 00:00:01 |
|   6 |     BITMAP CONVERSION FROM ROWIDS|		|	|	|	     |		|
|*  7 |      INDEX RANGE SCAN		 | IDX_EMP_MGR	|	|	|     1   (0)| 00:00:01 |
|   8 |     BITMAP CONVERSION FROM ROWIDS|		|	|	|	     |		|
|*  9 |      INDEX RANGE SCAN		 | IDX_EMP_DEPT |	|	|     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------
......

從上面的執行計劃中能夠看到關鍵字「BITMAP CONVERSION FROM ROWIDS」、「BITMAP AND」和「BITMAP CONVERSION TO ROWIDS」,這說明Oracle先分別對上述三個單鍵值的B*Tree索引IDX_EMP_MGR、IDX_EMP_DEPT和PK_EMP用映射函數將其中的ROWID轉換成了位圖,而後對轉換後的位圖執行了BITMAP AND(位圖按位與)布爾運算,最後將布爾運算的結果再次用映射函數轉換成了ROWID並回表獲得最終的執行結果。能走出這樣的執行計劃顯然是由於INDEX_COMBINE Hint生效了。

用映射函數將ROWID轉換成了位圖,而後再執行布爾運算,最後將布爾運算的結果再次用映射函數轉換成了ROWID並回表獲得最終的執行結果,這個過程在實際生產環境中的執行效率多是有問題的,可使用隱含參數_B_TREE_BITMAP_PLANS禁掉該過程當中的ROWID到位圖的轉換:

alter session set "_b_tree_bitmap_plans"=false;

scott@TEST>alter session set "_b_tree_bitmap_plans"=false;

Session altered.

scott@TEST>select /*+ index_combine(emp pk_emp idx_emp_mgr idx_emp_deptno) */ empno,ename,sal,job
  2    from emp
  3   where empno=7369 and mgr=7902 and deptno=20;

     EMPNO ENAME				 SAL JOB
---------- ------------------------------ ---------- ---------------------------
      7369 SMITH				 800 CLERK


Execution Plan
----------------------------------------------------------
Plan hash value: 2949544139

--------------------------------------------------------------------------------------
| Id  | Operation		    | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT	    |	     |	   1 |	  29 |	   1   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| EMP    |	   1 |	  29 |	   1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN	    | PK_EMP |	   1 |	     |	   0   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
......

從上面的執行計劃中能夠看出沒有出現BITMAP相關的關鍵字,即INDEX_COMBINE Hint被Oracle忽略了。

五、INDEX_FFS

INDEX_FFS是針對單個目標表的Hint,它的含義是讓優化器對目標表上的目標索引執行索引快速全掃描操做。注意,索引快速全掃描能成立的前提條件是SELECT語句中全部的查詢列都存在於目標索引中,即經過掃描目標索引就能夠獲得全部的查詢列而不用回表。

格式有以下三種:

格式1 /*+ INDEX_FFS(目標表 目標索引) */

格式2 /*+ INDEX_FFS(目標表 目標索引1 目標索引2 …… 目標索引n) */

格式3 /*+ INDEX_FFS(目標表) */

上述3種格式的含義和INDEX中對應格式的含義相同。

使用範例:

select /*+ index_ffs(emp pk_emp) */ empno
  from emp;

select /*+ index_ffs(emp idx_emp_1 idx_emp_2) */ empno
  from emp
 where mgr=7902 and deptno=20;
--create index idx_emp_1 on emp(mgr,deptno,1);
--create index idx_emp_2 on emp(mgr,deptno,2);

select /*+ index_ffs(emp) */ empno
  from emp;

看下面的實例:

scott@TEST>select empno from emp;

14 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 179099197

---------------------------------------------------------------------------
| Id  | Operation	 | Name   | Rows  | Bytes | Cost (%CPU)| Time	  |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT |	  |    10 |    40 |	1   (0)| 00:00:01 |
|   1 |  INDEX FULL SCAN | PK_EMP |    10 |    40 |	1   (0)| 00:00:01 |
---------------------------------------------------------------------------
......
scott@TEST>select /*+ index_ffs(emp) */empno from emp;

14 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 366039554

-------------------------------------------------------------------------------
| Id  | Operation	     | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |	      |    10 |    40 |     2	(0)| 00:00:01 |
|   1 |  INDEX FAST FULL SCAN| PK_EMP |    10 |    40 |     2	(0)| 00:00:01 |
-------------------------------------------------------------------------------
......

六、INDEX_JOIN

INDEX_JOIN是針對單個目標表的Hint,它的含義是讓優化器對目標表上的多個目標索引執行INDEX JOIN操做。INDEX JOIN能成立的前提條件是SELECT語句中全部的查詢列都存在於目標表上的多個目標索引中,即經過掃描這些索引就能夠獲得全部的查詢列而不用回表。

格式以下:

格式1 /*+ INDEX_JOIN(目標表 目標索引1 目標索引2 …… 目標索引n) */

格式2 /*+ INDEX_JOIN */

上述兩種格式的含義與INDEX_COMBINE Hint中對應格式的含義相同。

使用範例:

select /*+ index_join(emp pk_emp idx_emp_mgr) */ empno,mgr
  from emp
 where empno>7369 and mgr<7902;
 
select /*+ index_join(emp) */ empno,mgr
  from emp
 where empno>7369 and mgr<7902;

來看下面的實例:

scott@TEST>select empno,mgr
  2    from emp
  3   where empno>7369 and mgr<7902;

12 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 2059184959

-------------------------------------------------------------------------------------------
| Id  | Operation		    | Name	  | Rows  | Bytes | Cost (%CPU)| Time	  |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT	    |		  |    10 |    80 |	2   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| EMP	  |    10 |    80 |	2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN	    | IDX_EMP_MGR |    11 |	  |	1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
......
scott@TEST>select /*+ index_join(emp) */ empno,mgr
  2    from emp
  3   where empno>7369 and mgr<7902;

12 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 3030719951

---------------------------------------------------------------------------------------
| Id  | Operation	   | Name	      | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |		      |    10 |    80 |     3  (34)| 00:00:01 |
|*  1 |  VIEW		   | index$_join$_001 |    10 |    80 |     3  (34)| 00:00:01 |
|*  2 |   HASH JOIN	   |		      |       |       | 	   |	      |
|*  3 |    INDEX RANGE SCAN| IDX_EMP_MGR      |    10 |    80 |     2  (50)| 00:00:01 |
|*  4 |    INDEX RANGE SCAN| PK_EMP	      |    10 |    80 |     2  (50)| 00:00:01 |
---------------------------------------------------------------------------------------
......

七、AND_EQUAL

AND_EQUAL是針對單個目標表的Hint,它的含義是讓優化器對目標表上的多個目標索引執行INDEX MERGE操做。INDEX MERGE能成立的前提條件是目標SQL的where條件裏出現了多個針對不一樣單列的等值條件,而且這些列上都有單鍵值的索引。另外,在Oracle數據庫裏,可以作INDEX MERGE的索引數量的最大值是5。

格式以下:

/*+ AND_EQUAL(目標表 目標索引1 目標索引2 …… 目標索引n)*/

使用範例:

select /*+ and_equal(emp idx_emp_mgr idx_emp_dept) */ empno,mgr
  from emp
 where deptno=20 and mgr=7902;

看下面的實例:

scott@TEST>select empno,mgr
  2    from emp
  3   where deptno=20 and mgr=7902;


Execution Plan
----------------------------------------------------------
Plan hash value: 2059184959

-------------------------------------------------------------------------------------------
| Id  | Operation		    | Name	  | Rows  | Bytes | Cost (%CPU)| Time	  |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT	    |		  |	1 |    11 |	2   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| EMP	  |	1 |    11 |	2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN	    | IDX_EMP_MGR |	2 |	  |	1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
......
scott@TEST>select /*+ and_equal(emp idx_emp_mgr idx_emp_dept) */ empno,mgr
  2    from emp
  3   where deptno=20 and mgr=7902;


Execution Plan
----------------------------------------------------------
Plan hash value: 3295440569

--------------------------------------------------------------------------------------------
| Id  | Operation		    | Name	   | Rows  | Bytes | Cost (%CPU)| Time	   |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT	    |		   |	 1 |	11 |	 3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| EMP	   |	 1 |	11 |	 3   (0)| 00:00:01 |
|   2 |   AND-EQUAL		    |		   |	   |	   |		|	   |
|*  3 |    INDEX RANGE SCAN	    | IDX_EMP_MGR  |	 2 |	   |	 1   (0)| 00:00:01 |
|*  4 |    INDEX RANGE SCAN	    | IDX_EMP_DEPT |	 5 |	   |	 1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
......

4、與錶鏈接順序相關的Hint

一、ORDERED

ORDERED是針對多個目標表的Hint,它的含義是讓優化器對多個目標表執行錶鏈接操做時,執照它們在目標SQL的where條件中出現的順序從左到右依次進行鏈接。

格式以下:

/*+ ORDERED */

使用範例:

select /*+ ordered */ e.ename,j.job,e.sal,d.deptno
  from emp e,jobs j,dept d 
 where e.empno=j.empno
   and e.deptno=d.deptno
   and d.loc='CHICAGO'
 order by e.ename;

實例:

scott@TEST>select e.ename,j.job,e.sal,d.deptno
  2    from emp e,jobs j,dept d 
  3   where e.empno=j.empno
  4     and e.deptno=d.deptno
  5     and d.loc='CHICAGO'
  6   order by e.ename;

6 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 4113290228

-----------------------------------------------------------------------------------------------
| Id  | Operation                      | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |              |     5 |   235 |     9  (23)| 00:00:01 |
|   1 |  SORT ORDER BY                 |              |     5 |   235 |     9  (23)| 00:00:01 |
|*  2 |   HASH JOIN                    |              |     5 |   235 |     8  (13)| 00:00:01 |
|   3 |    NESTED LOOPS                |              |       |       |            |          |
|   4 |     NESTED LOOPS               |              |     5 |   140 |     4   (0)| 00:00:01 |
|*  5 |      TABLE ACCESS FULL         | DEPT         |     1 |    11 |     3   (0)| 00:00:01 |
|*  6 |      INDEX RANGE SCAN          | IDX_EMP_DEPT |     5 |       |     0   (0)| 00:00:01 |
|   7 |     TABLE ACCESS BY INDEX ROWID| EMP          |     5 |    85 |     1   (0)| 00:00:01 |
|   8 |    TABLE ACCESS FULL           | JOBS         |    14 |   266 |     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
......
scott@TEST>select /*+ ordered */ e.ename,j.job,e.sal,d.deptno
  2    from emp e,jobs j,dept d 
  3   where e.empno=j.empno
  4     and e.deptno=d.deptno
  5     and d.loc='CHICAGO'
  6   order by e.ename;

6 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 3031293267

-----------------------------------------------------------------------------------------
| Id  | Operation                      | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |        |     5 |   235 |    11  (28)| 00:00:01 |
|   1 |  SORT ORDER BY                 |        |     5 |   235 |    11  (28)| 00:00:01 |
|*  2 |   HASH JOIN                    |        |     5 |   235 |    10  (20)| 00:00:01 |
|   3 |    MERGE JOIN                  |        |    14 |   504 |     6  (17)| 00:00:01 |
|   4 |     TABLE ACCESS BY INDEX ROWID| EMP    |    14 |   238 |     2   (0)| 00:00:01 |
|   5 |      INDEX FULL SCAN           | PK_EMP |    14 |       |     1   (0)| 00:00:01 |
|*  6 |     SORT JOIN                  |        |    14 |   266 |     4  (25)| 00:00:01 |
|   7 |      TABLE ACCESS FULL         | JOBS   |    14 |   266 |     3   (0)| 00:00:01 |
|*  8 |    TABLE ACCESS FULL           | DEPT   |     1 |    11 |     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
......
scott@TEST>select /*+ ordered */ e.ename,j.job,e.sal,d.deptno
  2    from emp e,dept d,jobs j
  3   where e.empno=j.empno
  4     and e.deptno=d.deptno
  5     and d.loc='CHICAGO'
  6   order by e.ename;

6 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 1175157407

-----------------------------------------------------------------------------------------------
| Id  | Operation                      | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |              |     5 |   235 |    11  (28)| 00:00:01 |
|   1 |  SORT ORDER BY                 |              |     5 |   235 |    11  (28)| 00:00:01 |
|*  2 |   HASH JOIN                    |              |     5 |   235 |    10  (20)| 00:00:01 |
|   3 |    MERGE JOIN                  |              |     5 |   140 |     6  (17)| 00:00:01 |
|   4 |     TABLE ACCESS BY INDEX ROWID| EMP          |    14 |   238 |     2   (0)| 00:00:01 |
|   5 |      INDEX FULL SCAN           | IDX_EMP_DEPT |    14 |       |     1   (0)| 00:00:01 |
|*  6 |     SORT JOIN                  |              |     1 |    11 |     4  (25)| 00:00:01 |
|*  7 |      TABLE ACCESS FULL         | DEPT         |     1 |    11 |     3   (0)| 00:00:01 |
|   8 |    TABLE ACCESS FULL           | JOBS         |    14 |   266 |     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
......

從上面的執行計劃能夠看出不使用ordered Hint時表掃描的順序是DEPT->EMP->JOBS,可是使用ordered Hint後,表掃描的順序變爲了EMP->JOBS->DEPT與目標SQL中的順序一致了,在修改了目標SQL文本以後表的掃描順序也相應地變爲了EMP->DEPT->JOBS。

二、LEADING

LEADING是針對多個目標表的Hint,它的含義是讓優化器將咱們指定的多個表的鏈接結果做爲目標SQL錶鏈接過程當中的驅動結果集,而且將LEADING Hint中從左至右出現的第一個目標表做爲整個錶鏈接過程當中的首個驅動表。

LEADING比ORDERED要溫和一些,由於它只是指定了首個驅動表和驅動結果集,沒有像ORDERED那樣徹底指定了錶鏈接的順序,也就是說LEADING給了優化器更大的調整餘地。

當LEADING Hint中指定的表並不能做爲目標SQL的鏈接過程當中的驅動表或者驅動結果集時,Oracle會忽略該Hint。

格式以下:

/*+ LEADING(目標表1 目標表2 …… 目標表n) */

使用範例:

select /*+ leading(t e) */ e.ename,j.job,e.sal,d.deptno
  from emp e,jobs j,dept d,emp_temp t 
 where e.empno=j.empno
   and e.deptno=d.deptno
   and d.loc='CHICAGO'
   and e.ename=t.ename
 order by e.ename;

實例:

--不使用Hint
scott@TEST>select e.ename,j.job,e.sal,d.deptno
  2    from emp e,jobs j,dept d,emp_temp t 
  3   where e.empno=j.empno
  4     and e.deptno=d.deptno
  5     and d.loc='CHICAGO'
  6     and e.ename=t.ename
  7   order by e.ename;

6 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 558051962

------------------------------------------------------------------------------------------------
| Id  | Operation                       | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |              |     5 |   270 |    12  (17)| 00:00:01 |
|   1 |  SORT ORDER BY                  |              |     5 |   270 |    12  (17)| 00:00:01 |
|*  2 |   HASH JOIN                     |              |     5 |   270 |    11  (10)| 00:00:01 |
|*  3 |    HASH JOIN                    |              |     5 |   235 |     8  (13)| 00:00:01 |
|   4 |     NESTED LOOPS                |              |       |       |            |          |
|   5 |      NESTED LOOPS               |              |     5 |   140 |     4   (0)| 00:00:01 |
|*  6 |       TABLE ACCESS FULL         | DEPT         |     1 |    11 |     3   (0)| 00:00:01 |
|*  7 |       INDEX RANGE SCAN          | IDX_EMP_DEPT |     5 |       |     0   (0)| 00:00:01 |
|   8 |      TABLE ACCESS BY INDEX ROWID| EMP          |     5 |    85 |     1   (0)| 00:00:01 |
|   9 |     TABLE ACCESS FULL           | JOBS         |    14 |   266 |     3   (0)| 00:00:01 |
|  10 |    TABLE ACCESS FULL            | EMP_TEMP     |    14 |    98 |     3   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------
......
--使用LEADING Hint
scott@TEST>select /*+ leading(t e) */ e.ename,j.job,e.sal,d.deptno
  2    from emp e,jobs j,dept d,emp_temp t 
  3   where e.empno=j.empno
  4     and e.deptno=d.deptno
  5     and d.loc='CHICAGO'
  6     and e.ename=t.ename
  7   order by e.ename;

6 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 937897748

----------------------------------------------------------------------------------
| Id  | Operation             | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |          |     5 |   270 |    15  (20)| 00:00:01 |
|   1 |  SORT ORDER BY        |          |     5 |   270 |    15  (20)| 00:00:01 |
|*  2 |   HASH JOIN           |          |     5 |   270 |    14  (15)| 00:00:01 |
|*  3 |    HASH JOIN          |          |     5 |   175 |    10  (10)| 00:00:01 |
|*  4 |     HASH JOIN         |          |    14 |   336 |     7  (15)| 00:00:01 |
|   5 |      TABLE ACCESS FULL| EMP_TEMP |    14 |    98 |     3   (0)| 00:00:01 |
|   6 |      TABLE ACCESS FULL| EMP      |    14 |   238 |     3   (0)| 00:00:01 |
|*  7 |     TABLE ACCESS FULL | DEPT     |     1 |    11 |     3   (0)| 00:00:01 |
|   8 |    TABLE ACCESS FULL  | JOBS     |    14 |   266 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------------
......
--使用Ordered Hint
scott@TEST>select /*+ ordered */ e.ename,j.job,e.sal,d.deptno
  2    from emp e,jobs j,dept d,emp_temp t 
  3   where e.empno=j.empno
  4     and e.deptno=d.deptno
  5     and d.loc='CHICAGO'
  6     and e.ename=t.ename
  7   order by e.ename;

6 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 2459794491

--------------------------------------------------------------------------------------------
| Id  | Operation                       | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |          |     5 |   270 |    14  (22)| 00:00:01 |
|   1 |  SORT ORDER BY                  |          |     5 |   270 |    14  (22)| 00:00:01 |
|*  2 |   HASH JOIN                     |          |     5 |   270 |    13  (16)| 00:00:01 |
|*  3 |    HASH JOIN                    |          |     5 |   235 |    10  (20)| 00:00:01 |
|   4 |     MERGE JOIN                  |          |    14 |   504 |     6  (17)| 00:00:01 |
|   5 |      TABLE ACCESS BY INDEX ROWID| EMP      |    14 |   238 |     2   (0)| 00:00:01 |
|   6 |       INDEX FULL SCAN           | PK_EMP   |    14 |       |     1   (0)| 00:00:01 |
|*  7 |      SORT JOIN                  |          |    14 |   266 |     4  (25)| 00:00:01 |
|   8 |       TABLE ACCESS FULL         | JOBS     |    14 |   266 |     3   (0)| 00:00:01 |
|*  9 |     TABLE ACCESS FULL           | DEPT     |     1 |    11 |     3   (0)| 00:00:01 |
|  10 |    TABLE ACCESS FULL            | EMP_TEMP |    14 |    98 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
......

從上面的執行計劃能夠看出不使用Hint時表掃描順序是DEPT->EMP->JOBS->EMP_TEMP;使用LEADING Hint時表掃描順序是EMP_TEMP->EMP->DEPT->JOBS,EMP_TEMP作首個驅動表和表EMP的鏈接結果作爲驅動結果集,與Hint要求一致。;使用Ordered Hint時表掃描順序是EMP->JOBS->DEPT->EMP_TEMP,與SQL中順序一致。


參考《基於Oracle的SQL優化》

官方文檔:http://docs.oracle.com/cd/E11882_01/server.112/e41573/hintsref.htm#PFGRF005

相關文章
相關標籤/搜索