【DB筆試面試573】在Oracle中,常用Hint有哪些?
- 2019 年 10 月 10 日
- 筆記
♣
題目部分
在Oracle中,常用Hint有哪些?
♣
答案部分
Oracle的Hint是用來提示Oracle的優化器,用來選擇用戶期望的執行計劃。在許多情況下,Oracle默認的執行方式並不總是最優的,只不過由於平時操作的數據量比較小,所以,好的執行計劃與差的執行計劃所消耗的時間差異不大,用戶感覺不到而已。但對於書寫操作大數據量的SQL而言,其SQL的書寫則需要先了解一下執行計劃是否最優或滿足生產需要。通常當從開發環境遷移到生產環境下時,往往會出現此類情況。
例如:假設有一張客戶表,在客戶類別上有索引。如果想查找某一類別用戶,而該類別用戶佔總數的比例高達90%,那麼此時採用全表掃描方式將會比索引掃描方式快。如果不使用Hint,那麼Oracle很可能會選擇使用索引方式來執行。
使用Hint可以實現以下功能:
(1)改變SQL中的表的關聯順序。
(2)改變SQL中的表的關聯方式。
(3)實現並行方式執行DML、DDL以及SELECT語句。
(4)改變表的訪問路徑(數據讀取方式)。
(5)調整查詢轉換類型,重寫SQL。
(6)調整優化器優化目標。
(7)調整優化器類型。
Oracle推出了一個隱含參數「_OPTIMIZER_IGNORE_HINTS」,取值為TRUE或FALSE,預設值是FALSE。Oracle可以通過將該隱含參數設置為TRUE,使得Oracle優化器忽略語句中所有的Hint。顯然,Oracle提供此參數的目的就是在不修改應用的前提下,忽略所有Hint,讓Oracle優化器自己來選擇執行路徑。
Hint的語法格式如下所示:
{SELECT | INSERT | UPDATE | DELETE | MERGE} /*+ <具體的Hint內容>*/
關於Hint需要注意以下幾點:
l Hint中第一個星號(*)和加號(+)之間不能有空格。
l Hint中加號(+)和具體的Hint內容之間可以有空格,也可以沒有空格,但通常為了規範和區別於注釋建議加上空格。
l Hint中的具體內容可以是單個Hint,也可以是多個Hint的組合,如果是後者,那麼各個Hint間至少需要用1個空格來彼此分隔。
l Hint必須緊隨關鍵字SELECT、INSERT、UPDATE、DELETE或MERGE之後。
l 如果在目標SQL中使用了Hint,那麼就意味著自動啟用了CBO,即Oracle會以CBO來解析含Hint的目標SQL。但是對於RULE和DRIVING_SITE來說,它們可以在RBO下使用,而且不自動啟用CBO。
l Hint中指定具體對象時(比如指定表名或索引名),不能帶上該對象所在SCHEMA的名稱,即使該SQL文本中己經有對應的SCHEMA名稱。
l Hint中指定具體表名時,如果該表在對應SQL文本中有別名,那麼在Hint中應該使用該表的別名。
l 對於簡單的SQL語句一般只有一個查詢塊(Query Block),那麼在其上設置Hint其作用範圍就是該語句塊,而對於複雜的有多個查詢語句的SQL語句(例如查詢中用到了子查詢、內聯視圖、集合等操作時),各個Hint的作用域是不同的。Hint生效的範圍僅限於它本身所在的查詢塊,如果在Hint中不指定該Hint生效的查詢塊,那麼Oracle會默認認為它生效的範圍是指該Hint所處於的查詢塊。
l 由於各種原因導致Hint被Oracle忽略後,Oracle並不會給出任何提示或者警告,更不會報錯,目標SQL依然可以正常執行。導致Hint失效的原因通常有:
① 使用的Hint有語法或者拼寫錯誤。
② 使用的Hint是無效的(例如,在非等值連接中使用了USE_HASH)。
③ 使用的Hint是自相矛盾的(例如,即指定了FULL又指定了INDEX_FFS),但Oracle只是將自相矛盾的Hint全部忽略掉,而組合Hint中的其它Hint依然有效。
④ 使用的Hint受到了查詢轉換的干擾。
⑤ 依據Hint執行的結果是錯誤的(例如在非空的索引列上計算行數)。
⑥ 使用的Hint受到了保留關鍵字的干擾。Oracle在解析Hint的時候,從左到右進行,如果遇到一個詞是Oracle關鍵字或保留字,那麼Oracle將忽略這個詞以及之後的所有詞。如果遇到的一個詞既不是關鍵字也不是Hint,那麼就忽略該詞。如果遇到的詞是有效的Hint,那麼就會保留該Hint。Oracle的保留字或者關鍵字可以通過視圖V$RESERVED_WORDS來查詢。由此可以知道下面5條SQL語句中只有1和4中的APPEND提示是起作用的。
1. INSERT /*+ APPEND,PARALLEL(T1) */ INTO T1 SELECT * FROM T2; 2. INSERT /*+ PARALLEL(T1), APPEND */ INTO T1 SELECT * FROM T2; 3. INSERT /*+ THIS IS APPEND */ INTO T1 SELECT * FROM T2; 4. INSERT /*+ THIS APPEND */ INTO T1 SELECT * FROM T2; 5. INSERT /*+ NOLOGGING APPEND */ INTO T1 SELECT * FROM T2;
因為「IS」是一個關鍵字,「,」(逗號)也是一個關鍵字,所以,上面的第2和第3條SQL,Oracle解析時,當遇到「,」和「IS」時,就忽略了後面的所有Hint。在第4條SQL中,THIS並不是一個關鍵字,所以,APPEND提示有效。為了避免這樣的情況發生,當在SQL中書寫Hint時,在/*+ */這種結構內只寫Hint,而不要寫逗號,或者是其它的注釋。如果要對SQL寫注釋,那麼可以在專門的注釋結構中書寫。
在SQL語句優化過程中,經常會用到Hint,通過在SQL語句中設置Hint從而影響SQL的執行計劃,通過V$SQL_HINT視圖可以查詢所有的Hint,下表是一些常用的Hint:
表 3-19 常用Hint介紹

建議對上表中每個Hint都做相關的實驗來深入學習,而本書不再詳述。最後需要說明一下有關NOLOGGING的錯誤使用情況。下面幾條SQL都是使用NOLOGGING時的錯誤用法:
INSERT INTO T1 NOLOGGING; INSERT INTO T1 SELECT * FROM T2 NOLOGGING; INSERT /*+ NOLOGGING */ INTO T1 VALUES ('0'); INSERT /*+ NOLOGGING */ INTO T1 SELECT * FROM T2; DELETE /*+ NOLOGGING */ FROM T1; UPDATE /*+ NOLOGGING */ T1 SET A='1';
實際上,上述所有的SQL沒有一個能夠實現「不產生」日誌的數據更改操作。事實上,NOLOGGING並不是Oracle的一個有效的Hint,而是一個SQL關鍵字,通常用於DDL語句中。這裡NOLOGGING相當於給SELECT的表指定了一個別名為「NOLOGGING」。下面是NOLOGGING的一些正確用法:
CREATE TABLE T1 NOLOGGING AS SELECT * FROM T2; CREATE INDEX T1_IDX ON T1(A) NOLOGGING; ALTER INDEX T1_IDX REDUILD ONLINE NOLOGGING; ALTER TABLE T1 NOLOGGING;
若面試官問如何強制一個SQL語句使用索引,此時就可以回答使用Hint,/*+INDEX(TABLE INDEX_NAME)*/來完成。
& 說明:
有關Hint的更多內容可以參考我的BLOG:http://blog.itpub.net/26736162/viewspace-2125011/、http://blog.itpub.net/26736162/viewspace-2125709/
本文選自《Oracle程式設計師面試筆試寶典》,作者:李華榮。