Oracle中的sql hint

黑色与褐色發表於2024-03-07

Hint是Oracle資料庫提供的一種機制用來告訴最佳化器按照hint告訴它的方式生成執行計劃,是sql最佳化中常用的一個手段。

基於代價的最佳化器,在絕大多數情況下會選擇正確的最佳化器。但是有時候會選擇效率很差的執行計劃,使某個語句變得很慢,此時就需要透過hint告訴最佳化器使用指定的存取路徑或者連線型別生成執行計劃,從而使語句高效地執行。

{DELETE|INSERT|SELECT|UPDATE}/*+ hint [text] [hint[text]]*/
  or
{DELETE|INSERT|SELECT|UPDATE} --+ hint [text] [hint[text]]

hint提示只能出現在4個關鍵字後面;
“+”號表示此註釋是一個提示,必須緊跟“/*”,且中間不能有空格;
如果包含多個提示,則每個提示之間需要用一個或多個空格隔開;
text是其他說明hint的註釋性文字。
hint中的語法錯誤不會報錯,如果解析器不能解析,就會把它看做一個普通的註釋處理。

  1. 最佳化器相關的

OPT_PARAM:作用是使某條語句中指定某個系統引數值;
SELECT /*+ OPT_PARAM('_optimizer_unnest_disjunctive_subq' 'FALSE') */ ... FROM ...

ALL_ROWS:實現查詢語句整體最最佳化而引導最佳化器制定最少成本的執行計劃。最佳化器會選擇一條可最快檢索所有查詢行的路徑,代價就是檢索一行時,速度會很慢。
SELECT /*+ALL+_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';

FIRST_ROWS:為獲得最佳響應時間而引導最佳化器制定最少成本的執行計劃。這個提示會使最佳化器選擇可最快檢索出查詢的第一行(或指定行)資料的路徑
SELECT /*+FIRST_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';

CHOOSE:依據SQL中所使用到的表的統計資訊存在與否,來決定使用RBO還是CBO。
SELECT /*+CHOOSE*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';

RULE:使用基於規則的最佳化器來實現最最佳化執行,即引導最佳化器根據優先順序規則來決定查詢條件中所使用到的索引或運算子的執行 順序來制定執行計劃。
SELECT /*+ RULE */ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';

  1. 和訪問路徑相關的

FULL:告訴最佳化器透過全表掃描方式訪問資料。
SELECT /*+FULL(A)*/ EMP_NO,EMP_NAM FROM BSEMPMS A WHERE EMP_NO='SCOTT';

CLUSTER:引導最佳化器透過掃描聚簇索引來從索引表中讀取資料。
SELECT /*+CLUSTER */ BSEMPMS.EMP_NO,DPT_NO FROM BSEMPMS,BSDPTMS   WHERE DPT_NO='TEC304' AND BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;

HASH:引導最佳化器按照雜湊掃描的方式從表中讀取資料。

INDEX:告訴最佳化器對指定表透過索引的方式訪問資料。當訪問資料會導致結果集不完整時,最佳化器將忽略這個Hint。
SELECT /*+INDEX(BSEMPMS SEX_INDEX) USE SEX_INDEX BECAUSE THERE ARE FEWMALE BSEMPMS */ FROM BSEMPMS WHERE SEX='M';

NO_INDEX:告訴最佳化器對指定表不允許使用索引。這個提示會禁止最佳化器使用指定索引。
select /*+ no_index(emp ind_emp_sal ind_emp_deptno)*/ * from emp where deptno=200 and sal>300;

INDEX_ASC:利用索引從表中讀取資料時,引導最佳化器對提示中所指定索引的索引列值按照升序使用範圍掃描。
SELECT /*+INDEX_ASC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE DPT_NO='SCOTT';

INDEX_COMBINE:告訴最佳化器強制選擇點陣圖索引。這個提示會使最佳化器合併表上的多個點陣圖索引,而不是選擇其中最好的索引(這是INDEX提示的用途)。還可以使用index_combine指定單個索引(對於指定點陣圖索引,該提示優先於INDEX提示)。對於B樹索引,可以使用AND_EQUAL提示而不是這個提示。
SELECT /*+INDEX_COMBINE(BSEMPMS SAL_BMI HIREDATE_BMI)*/ * FROM BSEMPMS   WHERE SAL<5000000 AND HIREDATE

INDEX_JOIN:索引關聯,當謂詞中引用的列上都有索引的時候,可以透過索引關聯的方式來訪問資料。這個提示可以將同一個表的各個不同索引進行合併,這樣就只需要訪問這些索引就可以了,節省了回表查詢的時間。但只能在基於代價的最佳化器中使用該提示。這個提示不僅允許只訪問表上的索引, 這樣可以掃描更少的程式碼塊,並且它
比使用索引並透過rowid掃描整個錶快5倍。
SELECT /*+INDEX_JOIN(BSEMPMS SAL_HMI HIREDATE_BMI)*/ SAL,HIREDATE   FROM BSEMPMS WHERE SAL<60000;

INDEX_DESC:利用索引從表中讀取資料時,引導最佳化器對提示中所指定索引的索引列值按照降序使用範圍掃描。
SELECT /*+INDEX_DESC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE DPT_NO='SCOTT';

INDEX_FFS:告訴最佳化器以INDEX FFS(index fast full scan)的方式訪問資料。INDEX_FFS提示會執行一次索引的快速全域性掃描。這個提示只訪問索引,而不是對應的表。只有查詢需要檢索的資訊都在索引上時,才使用這個提示。特別在表有很多列時,使用該提示可以極大地改善效能。
SELECT /*+INDEX_FFS(BSEMPMS IN_EMPNAM)*/ * FROM BSEMPMS WHERE DPT_NO='TEC305';

INDEX_SS:強制使用index skip scan的方式訪問索引。當在一個聯合索引中,某些謂詞條件並不在聯合索引的第一列時(或者謂詞並不在聯合索引的第一列時), 可以透過index skip scan來訪問索引獲得資料。當聯合索引第一列的唯一值很少時,使用這種方式比全表掃描的方式效率要高。
select/*+ inde_ss(a idx_1) */ *from tmp_t0 a,tmp_t1 b where 1=1 and a.id=b.id;
3. 和查詢轉換相關的

USE_CONCAT:將含有多個OR或者IN運算子所連線起來的查詢語句分解l為多個單一查詢語句,併為每個單一查詢語句選擇最最佳化查詢路徑,然後再將這些最最佳化查詢路徑結合在一起,以實現整體查詢語句的最最佳化目的。只有在驅動查詢條件中包含OR的時候,才可以使用該提示。
SELECT /*+USE_CONCAT*/ * FROM BSEMPMS WHERE DPT_NO='TDC506' AND SEX='M';

NO_EXPAND:引導最佳化器不要為使用OR運算子號(或IN運算子)的條件制定相互結合的執行計劃。正好和USE_CONCAT相反。
SELECT /*+NO_EXPAND*/ * FROM BSEMPMS WHERE DPT_NO='TDC506' AND SEX='M';

REWRITE:當表連線的物件是資料量比較大的表或者需要獲得使用統計函式處理過的結果時,為了提高執行速度可預先建立物化檢視。當使用者要求查詢某個查詢語句時,最佳化器會在從表中和從物化檢視中讀取資料的兩種方法中選擇一個更有效的方法來讀取資料。該執行方法稱之為查詢重寫。使用REWRITE提示引導最佳化器按照該方式執行。
select/*+ rewrite */ * from tmp_t0 a,tmp_t1 b where 1=1and a.id=b.id

MERGE:為了能以最優方式從檢視或者巢狀檢視中讀取資料,透過變換查詢語句來直接讀取檢視使用的基表資料,該過程被稱之為檢視合併。

SELECT /*+MERGE(V) */ A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS A (SELET DPT_NO   ,AVG(SAL) AS AVG_SAL FROM BSEMPMS B GROUP BY DPT_NO) V WHERE A.DPT_NO=V.DPT_NO   AND A.SAL>V.AVG_SAL;

UNNEST:提示最佳化器將子查詢轉換為連線的方式。也就是引導最佳化器合併子查詢和主查詢並且將其向連線型別轉換。
select a.id from tmp_t0 a where 1=1 and exists (select/*+ unnest */ 1 from tmp_t1 b where 1=1 and a.id=b.id );

NO_UNNEST:引導最佳化器讓子查詢能夠獨立地執行完畢之後再跟外圍的查詢做FILTER。
select a.id from tmp_t0 a where 1=1 and a.id in (select/*+ no_unnest */ b.id from tmp_t1 b);

PUSH_PRED:謂詞推入 ,當SQL語句中包含不能合併的檢視,同時檢視有謂詞過濾,CBO會將謂詞過濾條件推入檢視中。其目的就是讓最佳化器儘早可能地過濾掉無用的資料,從而提升查詢效能。
謂詞推入的前提是要有不能合併的檢視。
select/*+ push_pred(a) */* from v_tmp_t0_data a,tmp_t1 b where 1=1 and a.id= b.id and b.id=2;

NO_PUSH_PRED:使用該提示確保檢視或巢狀檢視以外的查詢條件不被推入到檢視內部。
select /*+ no_push_pred(emp_view_union) */emp.empno from emp,emp_view_union where emp.empno=emp_view_union.empno1 and emp.ename="FORD";

PUSH_SUBQ:使用該提示引導最佳化器為不能合併的子查詢制定執行計劃。

select /*+ push_subq(@tmp) */ rownum
        t.*,
        pm.id
        pm.in_price****
        c.justin_cat__name,
        pm.justin_pro_id
   from justin_pro t
   left join justin_cat c
     on c.id = t.justin_cat_id
   join justin_pro_mer pm
     on pm.justin_pro_id = t.id
    and pm.id = (select /*+QB_Name(tmp)*/ id
                   from justin_pro_mer p_m
                  where p_m.justin_pro_id = t.id
                    and p_m.CAN_SALE = :a
                    and rownum = :b)
  where t.is_deleted = :c
    and rownum <= :d
    and t.justin_pro_type <> :e
    and t.is_hot = :f;

NO_PUSH_SUBQ:使用該提示將引導最佳化器將不能實現合併的子查詢放在最後執行。

  1. 和表連線順序相關的

LEADING:在一個多表關聯的查詢中,這個Hint指定由哪個表作為驅動表,即告訴最佳化器首先要訪問那個表上的資料。
select/*+ use_nl(a,b) leading(a) */ * from tmp_t0 a,tmp_t1 b where 1=1 and a.id=b.id;

ORDERED:引導最佳化器按照FROM中所描述的表的順序執行連線。如果和LEADING提示被一起使用,則LEADING提示將被忽略。
select/*+ ordered */ * from tmp_t0 a,tmp_t1 b where 1=1 and a.id=b.id;

  1. 和表連線操作相關的

USE_NL:使用該提示引導最佳化器按照巢狀迴圈連線方式執行表連線。它只是指出表連線的方式,對於表連線順序不會有任何影響。
SELECT /*+ORDERED USE_NL(BSEMPMS)*/ BSDPTMS.DPT_NO,BSEMPMS.EMP_NO,BSEMPMS.EMP_NAM FROM BSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;

USE_MERGE:引導最佳化器按照排序合併連線方式執行連線。在有必要的情況下,推薦將該提示與ORDERED提示一起使用。提示通常用於獲得查詢的最佳吞吐量。假設將兩個表連線在一起,從每個表返回的行集將被排序,然後再被合併(也就是合併排序),從而組成最終的結果集。由於每個行先被排序之後才進行合併,所以在給定查詢中檢索所有行時,速度將會最快。如果需要以最快速度返回第一行,就應該使用USE_NL提示。
select /*+ use_merge(t,t1) */ t.* from t,t1 where t.id=t1.id;

USE_HASH:該提示引導最佳化器按照雜湊連線方式執行連線。在執行雜湊連線時,如果由於某一邊的表比較小,從而可以在記憶體中實現雜湊連線,那麼就能夠獲得非常好的執行速度。
select /*+ use_hash(t,t1) leading(t,t1) */ t.* from t,t1 where t.id=t1.id;

  1. 和並行相關的

PARALLEL:指定SQL執行的並行度,這個值將會覆蓋表自身設定的並行度。如果這個值為default,CBO使用系統引數。從表中讀取大量資料和執行DML操作時使用該提示來指定SQL的並行操作。一般情況下需要在該提示中指定將要使用的並行執行緒個數。如果在該提示中沒有指定並行度的個數,則最佳化器將使用PARALLEL_THREADS_PER_CPU 引數所指定的值進行自動計算。如果在定義表時指定了PARALLEL,那麼在能夠使用並行操作的情況下,即使沒有使用該提示,最佳化器也會按照指定的並行級別選擇並行操作。 但是如果想在DELETE、INSERT、UPDATE、MERGE等DML操作中使用並行操作,則必須要在會話中設定ALTER SESSION ENABLE PARALLEL DML。在某個會話中所設定 的並行級別也可以被引用在內部的GROUP BY或者排序操作中。在並行操作中如果出現了某個限制要素,則該提示將被忽略。
select /*+ parallel(emp,4)*/ * from emp where deptno=200 and sal>300;

NOPARALLEL/NO_PARALLEL:在SQL語句禁止使用並行。在有些版本中用NO_PARALLEL提示來代替NOPARALLEL提示。
select /*+ no_parallel(t) */ count(*) from t;

PQ_DISTRIBUTE:為了提高並行連線的執行速度,使用該提示來定義使用何種方法在主從程序之間(例如生產者程序和消費者程序)分配各連線表的資料行。

PARALLEL_INDEX:為了按照並行操作的方式對分割槽索引進行索引範圍掃描而使用該提示,並且可以指定程序的個數。
select /*+ parallel_index(t_parallel,idx_t_parallel,2) */a from t_parallel where a=1;

  1. 其他相關的

APPEND:讓資料庫以直接載入的方式(direct load)將資料載入入庫。這個提示不會檢查當前是否有插入所需要的塊空間,相反它會直接將資料新增到新塊中。這樣會浪費空間,但可以提高插入的效能。需要注意的是,資料將被儲存在HWM之上的位置。
insert into t /*+ append */ select * from t;

APPEND_VALUES:在11.2中,Oracle新增了APPEND_VALUES提示,使得INSERT INTO VALUES語句也可以使用直接路徑插入。
INSERT /*+ APPEND_VALUES(A) */ INTO T_APPEND A VALUES (3, 'APPEND_VALUE');

CACHE:在全表掃描之後,資料塊將留在LRU列表的最活躍端。如果設定表的CACHE屬性,它的作用和HINT一樣。這個提示會將全表掃描全部快取到記憶體中。如果表很大,會佔用大量記憶體。因此適用於使用者經常訪問的較小的表。
SELECT /*+FULL(BSEMPMS) CAHE(BSEMPMS) */ EMP_NAM FROM BSEMPMS;

NOCACHE:引導最佳化器將透過全表掃描方式獲取的資料塊快取在LRU列表的最後位置,這樣可以讓資料庫例項快取中的這些資料塊被優先清除。這是最佳化器在Buffer Cache中管理資料塊的預設方法(僅針對全表掃描)。
SELECT /*+FULL(BSEMPMS) NOCAHE(BSEMPMS) */ EMP_NAM FROM BSEMPMS;

QB_NAME:使用該提示為查詢語句塊命名,在其他查詢語句塊可以直接使用該查詢語句塊的名稱。

SELECT /*+ QB_NAME(outer) */
            e.ename
     ,      e.sal
     FROM (
            SELECT /*+ QB_NAME(inline_view) */
                   *
            FROM   emp e
            WHERE e.sal > 300
            AND    e.deptno IN ( SELECT /*+ QB_NAME(dept_subquery) */ d.deptno
                                 FROM   dept d
                                 WHERE d.dname IN (‘SALES’,'ACCOUNTING’) )
           ) e;

DRIVING_SITE:這個提示在分散式資料庫操作中有用。指定表是處理連線所在的位置。可以限制透過網路處理的資訊量。此外,還可以建立遠端表的本地檢視來限制從遠端站點檢索的行。本地檢視應該有where子句,從而檢視可以在將行傳送回本地資料庫之前限制從遠端資料庫返回的行。
SELECT /*+DRIVING_SITE(DEPT)*/ * FROM BSEMPMS,DEPT@BSDPTMS WHERE BSEMPMS.DPT_NO=DEPT.DPT_NO;

DYNAMIC_SAMPLING:提示SQL執行時動態取樣的級別。這個級別為0~10,它將覆蓋系統預設的動態取樣級別。等級越高,所獲得統計資訊的準確率越高。該提示的功能就是為了確保將動態取樣原理應用在單個SQL中。
select /*+ dynamic_sampling(t 4) */ * from t where id > 1234

AND_EQUAL:這個提示會使最佳化器合併表上的多個索引,而不是選擇其中最好的索引(這是INDEX提示的用途)。這個提示與前面的INDEX_JOIN提示有區別,以此指定的合併索引隨後需訪問表,而INDEX_JOIN提示則只需訪問索引。如果發現需經常用到這個提示,可能需要刪除這些單個索引而改用一個組合索引。需要查詢條件裡面包括所有索引列,然後取得每個索引中得到的rowid列表。然後對這些物件做merge join,過濾出相同的rowid後再去表中獲取資料或者直接從索引中獲得資料。在10g中,and_equal已經廢棄了,只能透過hint才能生效。
select/*+ and_equal(a idx_1 idx_2) */ * from tmp_t0 a,tmp_t1 b where 1=1 and a.id=b.id and a.id=2 and a.name='Tom';

CARDINALITY:向最佳化器提供對某個查詢語句的整體或部分的預測基數值,並透過參考該基數值來為查詢語句制定執行計劃。如果在該提示中沒有指定表的名稱,則該基數值將被視為從該查詢語句所獲得的最終結果行數。
select /*+ cardinality(b 100000) */ a.object_name,a.object_type from a,b where a.object_id=b.object_id and b.object_id=1000;

具體來這看
https://docs.oracle.com/cd/B28359_01/server.111/b28274/hintsref.htm#PFGRF501

相關文章