優化器的模式用於決定在oracle中解析目標SQL時所使用的優化器的類型,以及決定當使用CBO時計算成本。sql
OPTIMIZER_MODE參數 決定優化器類型,選項有 RULE,CHOOSE,first_wors_n,first_rows 和 all_rows。session
rule : 表示oracle使用RBO模式來解析目標sql,oracle
choose:oracle 9i中optimizer_mode的默認值,他表示oracle在解析目標sql的時候有系統本身決定使用什麼模式的優化器,取決於目標對象是否用統計信息。具體來講,若是有目標表的統計信息則使用CBO,反之使用RBO。oop
first_rows_n :(n=1,10,100,1000),此時使用的是CBO優化器,且此時的CBO 在計算該SQL的各條執行計劃的時候着重返回前n條記錄,這就意味着採用的執行計劃不在是基於成本最小而是基於返回相應速度最快的執行計劃。優化
first_rows:oracle 9i的參數,表示在接續目標語句時聯合使用CBO,RBO。debug
ALL_rows:oracle 10g級之後版本優化器默認的參數,他表示使用CBO來解析目標SQL,此時CBO在計算執行路徑時 側重點在於最佳的i/o和cpu消耗。對象
實際上,成本的計算方式隨着優化器模式不一樣而不一樣,主要體如今ALL_ROWS和first_rows_n對成本的計算方式上。當優化器爲all_rows時,CBO計算成本側重點在於最佳吞吐上;當優化器爲first_rows_n時,CBO計算成本側重於最快相應速度。blog
結果集(Row Source)是指包含指定執行計劃結果的集合。對於優化器而言,結果集和目標sql的執行步驟對應。對於目標SQL執行計劃而言,其中某個執行步驟的輸出結果就是該執行步驟所對應的輸出結果集,同時該執行不走所對應的輸出結果集可能就是下一個執行步驟的輸入結果集。這樣一步一步執行下來,伴隨的就是結果集在各個執行步驟之間的傳遞,等目標SQL執行計劃的各個步驟所有執行完畢,最後的輸出結果集就是該SQL最終的執行結果。排序
對於CBO而言,對應執行計劃中的列(Rows)反映的就是CBO當前步驟輸出的結果集記錄數(Cardinality)的估計值。索引
訪問數據的路徑(Accent Path)對於優化器而言,它在解析目標SQL,獲得執行計劃相當重要的一點就是決定訪問表數據的方法。目標SQL所須要訪問數據通常採用2種方式:直接訪問和索引訪問。
訪問表的方式對應爲:全表掃描和rowid掃描。
全表掃描是指從表第一個區開始掃描一直到該表的高水位線爲止,隨着表數據越多,掃描所消耗的成本也就越高。若是對目標表不停的插入數據,當分配給該表的現有空間不足時高水位線就會向上移動(delete並不會下降高水位線),這就意味着即便表中的數據刪除了一部分,可是在進行全表掃描的時候仍是會掃描高水位線如下的數據塊,即便大部分數據塊時空的。
不是說 全表掃描很差,事實上oracle在作全表掃描操做時會使用多塊讀,這在目標表的數據量不大執行效率是很是高的。
Rowid是記錄所在行的物理存儲地址,那麼當經過rowid掃描數據時就經過訪問rowid直接訪問對應記錄。
咱們能夠經過dbms_rowid.rowid_relative_fno,dbms_rowid.rowid_block_number和dbms_rowid.rowid_row_number將rowid轉換成塊的物理地址。
SQL > select empno,ename,rowid,dbms_rowid.rowid_relative_fno(rowid)||'-'||dbms_rowid.rowid_block_number(rowid)||'-'||dbms
_rowid.rowid_row_number(rowid) loca from emp
EMPNO ENAME ROWID LOCA
---------- ---------- ------------------ ------------------------------
7900 SMITH AAATaLAAIAAAALTAAA 8-723-0
7901 ALLEN AAATaLAAIAAAALTAAB 8-723-1
7902 WARD AAATaLAAIAAAALTAAC 8-723-2
能夠看到,7900記錄存放在「8-723-0」 表示8號文件的723數據塊的第0行記錄。
基於索引(B-tree)訪問數據有以下優點:
(1)全部的索引葉子塊都在同一層,記他們距離索引根幾點的深度是相同的。這也意味着訪問索引葉子塊的任何一個索引健值所花費的時間幾乎是相同的。
(2)Oracle會保證全部的B樹索引都是自平衡的,既不可能出現不一樣的索引葉子塊不處於同一層現象。
(3)經過B樹索引訪問錶行記錄的效率並不會隨着表數據量增大而降低,即經過索引訪問數據塊是可控的,這是走索引掃描跟全表掃描最大的區別。b-tree索引結構就決定了oracle先訪問b-tree索引,而後獲得rowid,根據這個rowid在返回表訪問對應的數據行記錄。
常見索引掃描方式:
索引惟一掃描(index unique scan):針對惟一索引(unique index)的掃描,它僅僅適用於where條件裏等值查詢的SQL。由於掃描的對象是惟一性索引,因此索引惟一性掃描的結果最多隻有一條。
create unique index empno_idx on emp(empno);
select * from emp where empno=1901;
索引範圍掃描(index range scan):索引範圍掃描的結果可能會返回多條記錄。當目標索引的行數大於1時,索引就會走範圍掃描。
create index empno_idx on emp(empno);
select * from emp where empno<1901;
索引全掃描(index full scan):所謂的「索引全掃描」,就是指要掃描目標索引全部葉子塊的全部索引行。索引是有序的,因此索引全掃描返回的數據也是有序的。
select empno from emp;
索引全掃描的掃描結果是有序的這就決定了索引全掃描是不可以並行執行的,而且一般狀況下索引全掃描使用的是單塊讀。
索引快速掃描(index fast full scan)和索引全掃描同樣,索引快速掃描也須要掃描目標索引全部葉子塊的全部全部行。
索引快速掃描跟索引全掃描區別以下:
(1)索引快速全掃描只用於CBO
(2)索引快速全掃描可使用多快讀,也能夠並行執行。
(3)索引快速全掃描的執行結果不必定是有序的。
索引跳躍掃描(index skip scan):它使那些在where條件中沒有對目標索引的前導列指定查詢條件可是又對非前導列制定了查詢條件。
create index idx_employee on employee(gender,employee_id);
select * from employees where employee_id = 100;
錶鏈接:
錶鏈接的順序(兩兩鏈接):驅動表(outer table)和被驅動表(inner table)
錶鏈接類型:
內鏈接(Inner join)left join,rightjoin,full outer join,(+);
外連接(Outer join)left outer join,right outer join,full outer join
select * from t1 left outer join t2 on t1.col2 = t2.col2 等價於 select * from t1,t2 where t1.col2=t2.col2(+);
錶鏈接方法:
排序合併鏈接(Sort Merge Join) 兩個表在作錶鏈接是用排序操做和合並操做。
首先以where謂詞爲條件去過濾表T1,而後對過濾的結果進行排序,結果集記爲s1;
而後以where謂詞爲條件去過濾表T2,對過濾後的結果進行排序,結果集記爲s2;
最後對結果集s1 和結果集s2 執行合併操做,從中取出匹配記錄做爲最終結果。
一般狀況下,排序合併鏈接的執行效率遠不如哈希鏈接,但前者使用範圍更廣,由於哈希鏈接一般只能用於等值鏈接條件,而排序合併鏈接還能用於其餘鏈接條件。
嵌套循環鏈接(Nested Loops Join)是兩個表在作鏈接時依靠兩層嵌套循環來打得屁鏈接結果集的錶鏈接方法。
一般狀況下系統會按照必定規則來判斷誰是驅動表誰是被驅動表,驅動表用於外層循環,被驅動表用於內層循環。接着以where謂詞爲條件去訪問驅動表T1,獲得結果集爲s1;
而後遍歷驅動表結果集s1並同時遍歷被驅動表T2,拿s1第一條記錄依次去匹配被驅動表T2中的全部記錄,以此類推。經過謂詞條件去遍歷驅動表T1獲得的結果集s1,這是第一個循環,經過對結果集s1
的記錄依次匹配被驅動表T2這是第二個循環。很顯然,外層循環結果集有多少條記錄,遍歷被驅動表T2的內層循環就須要多少次,這就是所謂的「嵌套循環」的含義。
嵌套循環適用場景:
(1)若是驅動表被過濾後的結果集s1記錄少,同時被驅動表的鏈接咧上存在惟一索引(或者在被驅動表的鏈接咧上存在選擇性很好的非惟一索引),那麼此時使用嵌套循環執行效率是最高的。必定要確保結果集s1記錄少,即便被驅動表有惟一性索引,那麼效率也不會很高。
(2)只要驅動結果集記錄少,那麼就具有了嵌套循環的前提條件。
(3)嵌套循環鏈接有其餘鏈接所不具有的一個優勢:嵌套循環鏈接能夠實現快速響應,即它能夠第一時間先返回已經鏈接且知足條件的記錄數,而沒必要等到全部的操做所有完成才返回結果。
若是目標SQL的禪薰列並不能所有從被驅動表的相關索引中得到,那麼oracle在作完嵌套循環鏈接後還須要對被驅動表執行回表操做。
哈希鏈接(Hash Join)
兩個表依靠哈希運算獲得結果集的鏈接方法,至於hash join值使用在CBO中。
在10g及之後的版本,哈希鏈接受限於_hash_join_enabled參數,默認值爲true,固然也可使用hit(use_hash)來提示使用哈希鏈接。
表T1和表T2在通過謂詞where過濾後獲得的結果集最少的那個將被當作驅動表,,,,,(過程太繁雜,不寫了)。
適用場景:
(1)只適用於CBO模式,只適用於等值鏈接條件
(2)經過哈希獲得的結果集 大多狀況下是不排序的
(3)哈希鏈接的驅動表所對應的鏈接咧的可選擇性儘量好
(4)哈希很適用於大表跟小表作鏈接且鏈接結果集的記錄較多的狀況下效果更好。
(5)被選做驅動表的結果集若是比較小徹底能夠在內存中作運算的話,此時的執行效率很是高。
能夠經過00104事件來觀察目標SQL在作哈希鏈接時的大體過程和一些統計信息:
oradebug setmypid
oradebug event 10104 trace name context forever,level 1
set autotrace traceonly
select * from plat2.emp where empno=1900;
oradebug tracefile_name
笛卡爾鏈接(Cross Join)又稱爲笛卡爾乘積(Cartesian Product),它是一種表之間沒有任何鏈接條件時的鏈接方式。
笛卡爾鏈接其實是一種特殊的「合併鏈接」,相似於排序合併鏈接(Sort Merge Join),只是不須要排序,且在執行合併操做室沒有鏈接條件而已。
最後結果 9,正好是3*3。
通常出現笛卡爾鏈接大多狀況是表之間沒有鏈接條件或者表統計信息不許確致使,在工做中應該儘可能避免笛卡爾鏈接出現。
反鏈接(Anti Join)
當作子查詢展開時,Oracle常常會吧那些外部where條件爲 not exists,not in或 <> all的子查詢轉換成對應的反鏈接。
select * from t1 where col2 not in (select col2 from t2);
半鏈接(Semi Join)Oracle常常會把那些外部where條件爲exists,in或 = any的子查詢轉換成半鏈接。
星型鏈接(Star Join)
一般使用在數據倉庫中,它是由一個事實表(Fact Table)和多個維度表(Dimension Table)之間的鏈接。
index join
針對單表上的不一樣索引之間的鏈接。基於索引合併的狀況咱們通常都使用複合索引來解決效率的問題。
VIEW
在處理包含視圖的sql時,根據視圖是否能合併,分爲以下2種狀況:
視圖合併和視圖不能合併
FILTER
過濾器,是一種改良的嵌套循環鏈接
SORT
排序,主要包括:
sort aggregate
sort unique
sort join
sort group by
sort order by
buffer sort
使用執行計劃或者10032跟蹤SQL排序狀況
alter session set events '10032 trace name contents forever'
文件名就是<SID>_ora_<PID>.trc
CONCAT
其實即便in-list擴展(in-list expansion)或 or 擴展(or expansion)
select /*+use_concat*/ * from empno where empno in (1900,7900,7800);
union all 僅僅是簡單地將兩個結果集合並;union 將兩個結果集合並而後對結果集排序去重操做。
connect by層次查詢(Hierarchical Queries)
select empno,ename,mgr from emp start with empno = 7889 connect by prior empno=mgr;