本文介紹了Oracle數據庫里常見的執行計劃,使用的Oracle數據庫版本為11.2.0.1。
1、與表訪問相關的執行計劃
Oracle數據庫里與表訪問有關的兩種方法:全表掃描和ROWID掃描。反映在執行計劃上,與全表掃描對應的執行計劃中的關鍵字是“TABLE ACCESS FULL”,與ROWID掃描對應的執行計劃中關鍵字是“TABLE ACCESS BY USER ROWID”或“TABLE ACCESS BY INDEX ROWID”。
從實驗中可以看出,第一個SQL執行計劃走的是對表EMP的全表掃描,對應的關鍵字就是“TABLE ACCESS FULL”;第二個SQL的執行計劃走的是對表EMP的ROWID掃描,對應的關鍵字是“TABLE ACCESS BY USER ROWID”;第三個SQL的執行計劃走的是對表EMP的ROWID掃描,對應的關鍵字是“TABLE ACCESS BY INDEX ROWID”。注意如果ROWID來源于用戶手工指定則對應的是“TABLE ACCESS BY USER ROWID”;如果ROWID是來源于索引,則對應的是“TABLE ACCESS BY INDEX ROWID”。
2與B*Tree索引相關的執行計劃
Oracle數據庫里常見的與B*Tree索引訪問相關的方法,包括索引唯一掃描、索引范圍掃描、索引全掃描、索引快速全掃描和索引跳躍式掃描,反映在執行計劃上分別對應INDEX UNIQUE SCAN、INDEX RANGE SCAN、INDEX FULL SCAN、INDEX FAST FULL SCAN和INDEX SKIP SCAN。
用實驗查看相關執行計劃
第一個SQL的執行計劃走的是對索引IDX_UNI_EMP的索引唯一掃描,關鍵字是“INDEX UNIQUE SCAN”。
現在SQL的執行計劃是對索引IDX_EMP_1的索引范圍掃描,關鍵字是“INDEX RANGE SCAN”。
明明可以掃描索引IDX_EMP_1得到結果,卻選擇了全表掃描,就算使用Hint強制讓Oracle掃描索引IDX_EMP_1,結果卻是Hint失效了。
出現這個現象的原因是Oracle無論如何總會保證目標SQL結果的正確性,可能會得到錯誤結果的執行路徑Oracle是不會考慮的。對于索引IDX_EMP_1而言,它是一個單鍵值的B*Tree索引,所以NULL值不會存儲在其中,那么一量EMPLOYEE_ID出現了NULL值(雖然這里實際上并沒有NULL值),則掃描索引的結果就是漏掉那些EMPLOYEE_ID為NULL值的記錄,這也就意味著如果Oracle在執行上述SQL時選擇了掃描IDX_EMP_1,那么執行結果就有可能是不準的。在這種情況下,Oracle當然不會考慮掃描索引,即使我們使用了Hint。
如果想讓Oracle在執行上述SQL時掃描索引IDX_EMP_1,則必須將列EMPLOYEE_ID的屬性修改為NOT NULL。這就相當于告訴Oracle,這里列EMPLOYEE_ID上不會有NULL值,你就放心地掃描索引IDX_EMP_1吧。
從上面的輸出可以看出,現在SQL的執行計劃走的是對索引IDX_EMP_1的索引快速全掃描,對應的是“INDEX FAST FULL SCAN”。
現在加上強制走索引IDX_EMP_1的Hint,再次執行該SQL
可以看到現在SQL的執行計劃走的是對索引IDX_EMP_1的索引快速全掃描INDEX FULL SCAN(如果是在11.2.0.4版本上執行上以SQL可以以看到還是INDEX FAST FULL SCAN)
從上面輸出可以看出,SQL的執行計劃走的是對索引IDX_EMP_2的索引跳躍式掃描,對應“INDEXSKIP SCAN”。
3、與表連接相關的執行計劃
Oracle數據庫里常見的與表連接相關的一些方法:排序合并連接、嵌套循環連接、哈希連接等以及反連接和半連接
從上面的輸出可以看出,SQL的執行計劃走的是對表T1和T2的哈希連接,連接條件是t1.col2=t2.col2,對應的關鍵字是“HASH JOIN”。
使用強制走排序合并連接的Hint后再次執行SQL
從上面的輸出可以看出現在SQL的執行計劃走的是對表T1和T2的排序合并連接,對應的關鍵字是“MERGEJOIN”和“SORT JOIN”。
接著使用強制走嵌套循環連接的Hint后再次執行SQL
從上面的輸出可以看出現在SQL的執行計劃走的是對表T1和T2的嵌套循環連接,對應的關鍵字是“NESTEDLOOPS”
嵌套循環連接的驅動表是可以變的,我們使用Hint將上述SQL的驅動表改為T1再將執行SQL
從結果中可以看到,嵌套循環連接的驅動表確實已經變為T1
再看反連接的例子。首先將表T1和T2的連接列col2改為NOT NULL,以便能走出我們想要的反連接的執行計劃
從輸出內容上可以看出,SQL的執行計劃走的是對表T1和T2的哈希反連接,反連接在執行計劃中對應的關鍵字是“ANTI”,哈希反連接對應的就是“HASH JOIN ANTI”。
反連接的具體連接方法是可變的,這里使用Hint將SQL的反連接改為排序合并反連接
從輸出內容可以看出,SQL的執行計劃走的是對表T1和T2的排序合并反連接,對應的關鍵字是“MERGE JOIN ANTI”。
再使用Hint將SQL的反連接方法改為嵌套循環反連接
再看半連接的例子。
從輸出可以看出,SQL的執行計劃走的是對表T1和T2的哈希半連接,半連接在執行計劃中對應的關鍵字是“SEMI”,哈希半連接在執行計劃中對應的關鍵字是“HASH JOIN SEMI”。
半連接的具體連接方法是可變的,使用Hint將SQL的半連接方法改為排序合并半連接:
從輸出內容可以看出,SQL的執行計劃走的是對表T1和T2的排序合并半連接,對應的關鍵字是“MERGE JOIN SEMI”。
再使用Hint把SQL的半連接方法改為嵌套循環半連接:
從輸出內容可以看出,SQL的執行計劃走的是對表T1和T2的嵌套循環半連接,對應的關鍵字是“NESTED LOOPS SEMI”
4、關于位圖索引相關的執行計劃
Oracle數據庫里常見的與位圖索引訪問相關的方法包括如下這些類型:位圖索引單鍵值掃描、位圖索引范圍掃描、位圖索引全掃描、位圖索引快速全掃描、位圖按位與、位圖按位或、位圖按位減等。
Oracle在使用完位圖索引后通常會將最后的位圖運算結果轉化為ROWID,這一步轉換過程對應的執行計劃中的“BITMAP CONVERSION TO ROWIDS”。
從上面的輸出內容可以看出,SQL的執行計劃走的是對位圖索引IDX_B_REGION的位圖索引單鍵值掃描,對就的關鍵字是“BITMAP INDEX SINGLE VALUE”。
把SQL改寫為范圍查詢后再次執行
從輸出內容可以看出SQL走的執行計劃是對位圖索引IDX_B_REGION的位圖索引范圍掃描,對應的關鍵字是“BITMAP INDEX RANGE SCAN”。
去掉where條件,并且只查詢位圖索引IDX_B_REGION的索引鍵值列:
從輸出可以看出SQL走的執行計劃是對位圖索引IDX_B_REGION的位圖索引快速全掃描,對應的關鍵字是“BIT INDEX FAST FULL SCAN”。
執行如下SQL:
從輸出內容可以看出SQL走的執行計劃中,用到了位圖按位與操作,對應的關鍵字是“BITMAP AND”和位圖按位或操作,對應的關鍵字是“BITMAP OR”。
再構造位圖按位減的執行計劃,SQL如下:
從輸出的執行計劃中,位圖按位減的執行計劃對應的關鍵字是“BITMAP MINUX”。