本文介紹了Oracle數據庫里常見的執行計劃,使用的Oracle數據庫版本為11.2.0.1。
成都創新互聯服務項目包括興平網站建設、興平網站制作、興平網頁制作以及興平網絡營銷策劃等。多年來,我們專注于互聯網行業,利用自身積累的技術優勢、行業經驗、深度合作伙伴關系等,向廣大中小型企業、政府機構等提供互聯網行業的解決方案,興平網站推廣取得了明顯的社會效益與經濟效益。目前,我們服務的客戶以成都為中心已經輻射到興平省份的部分城市,未來相信會繼續擴大服務區域并繼續獲得客戶的支持與信任!
1、與表訪問相關的執行計劃
Oracle數據庫里與表訪問有關的兩種方法:全表掃描和ROWID掃描。反映在執行計劃上,與全表掃描對應的執行計劃中的關鍵字是“TABLE ACCESS FULL”,與ROWID掃描對應的執行計劃中關鍵字是“TABLE ACCESS BY USER ROWID”或“TABLE ACCESS BY INDEX ROWID”。
scott@MYDB>select empno,ename,rowid from emp where ename='SCOTT'; EMPNO ENAME ROWID ---------- ------------------------------ ------------------ 7788 SCOTT AAAR3xAAEAAAACXAAH
scott@MYDB>select empno,ename,rowid from emp where rowid='AAAR3xAAEAAAACXAAH'; EMPNO ENAME ROWID ---------- ------------------------------ ------------------ 7788 SCOTT AAAR3xAAEAAAACXAAH
scott@MYDB>select empno,ename,rowid from emp where empno=7788; EMPNO ENAME ROWID ---------- ------------------------------ ------------------ 7788 SCOTT AAAR3xAAEAAAACXAAH
從實驗中可以看出,第一個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。
用實驗查看相關執行計劃
zx@MYDB>create table employee (gender varchar2(1),employee_id number); Table created. zx@MYDB>insert into employee values('F',99); 1 row created. zx@MYDB>insert into employee values('F',100); 1 row created. zx@MYDB>insert into employee values('M',101); 1 row created. zx@MYDB>insert into employee values('M',102); 1 row created. zx@MYDB>insert into employee values('M',103); 1 row created. zx@MYDB>insert into employee values('M',104); 1 row created. zx@MYDB>insert into employee values('M',105); 1 row created. zx@MYDB>create unique index idx_uni_emp on employee(employee_id); Index created. zx@MYDB>select * from employee where employee_id=100; GEN EMPLOYEE_ID --- ----------- F 100
第一個SQL的執行計劃走的是對索引IDX_UNI_EMP的索引唯一掃描,關鍵字是“INDEX UNIQUE SCAN”。
zx@MYDB>drop index idx_uni_emp; Index dropped. zx@MYDB>create index idx_emp_1 on employee(employee_id); Index created. zx@MYDB>select * from employee where employee_id=100; GEN EMPLOYEE_ID --- ----------- F 100
現在SQL的執行計劃是對索引IDX_EMP_1的索引范圍掃描,關鍵字是“INDEX RANGE SCAN”。
zx@MYDB>truncate table employee; Table truncated. zx@MYDB>begin 2 for i in 1..10000 loop 3 insert into employee select decode(mod(i,2),0,'M','F'),i from dual; 4 end loop; 5 end; 6 / PL/SQL procedure successfully completed. zx@MYDB>zx@MYDB>commit; Commit complete. zx@MYDB>select gender,count(*) from employee group by gender; GEN COUNT(*) --- ---------- M 5000 F 5000 zx@MYDB>exec dbms_stats.gather_table_stats(ownname=>USER,tabname=>'EMPLOYEE',estimate_percent=>100,cascade=>true,no_invalidate=>false,method_opt=>'FOR ALL COLUMNS SIZE 1'); PL/SQL procedure successfully completed. zx@MYDB>set autotrace traceonly zx@MYDB>select employee_id from employee; 10000 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 2119105728 ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 10000 | 40000 | 7 (0)| 00:00:01 | | 1 | TABLE ACCESS FULL| EMPLOYEE | 10000 | 40000 | 7 (0)| 00:00:01 | ------------------------------------------------------------------------------ ...省略部分輸出
明明可以掃描索引IDX_EMP_1得到結果,卻選擇了全表掃描,就算使用Hint強制讓Oracle掃描索引IDX_EMP_1,結果卻是Hint失效了。
zx@MYDB>select /* +index(employee idx_emp_1) */employee_id from employee; 10000 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 2119105728 ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 10000 | 40000 | 7 (0)| 00:00:01 | | 1 | TABLE ACCESS FULL| EMPLOYEE | 10000 | 40000 | 7 (0)| 00:00:01 | ------------------------------------------------------------------------------ ...省略部分輸出
出現這個現象的原因是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吧。
zx@MYDB>alter table employee modify employee_id not null; Table altered. zx@MYDB>select employee_id from employee; 10000 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 3918702848 ---------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 10000 | 40000 | 7 (0)| 00:00:01 | | 1 | INDEX FAST FULL SCAN| IDX_EMP_1 | 10000 | 40000 | 7 (0)| 00:00:01 | ---------------------------------------------------------------------------------- ...省略部分輸出
從上面的輸出可以看出,現在SQL的執行計劃走的是對索引IDX_EMP_1的索引快速全掃描,對應的是“INDEX FAST FULL SCAN”。
現在加上強制走索引IDX_EMP_1的Hint,再次執行該SQL
zx@MYDB>select /*+index(employee idx_emp_1) */employee_id from employee; 10000 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 438557521 ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 10000 | 40000 | 20 (0)| 00:00:01 | | 1 | INDEX FULL SCAN | IDX_EMP_1 | 10000 | 40000 | 20 (0)| 00:00:01 | ------------------------------------------------------------------------------ ...省略部分輸出可以看到現在SQL的執行計劃走的是對索引IDX_EMP_1的索引快速全掃描INDEX FULL SCAN(如果是在11.2.0.4版本上執行上以SQL可以以看到還是INDEX FAST FULL SCAN)
zx@MYDB>drop index idx_emp_1; Index dropped. zx@MYDB>create index idx_emp_2 on employee(gender,employee_id); Index created. zx@MYDB>select * from employee where employee_id=101;
從上面輸出可以看出,SQL的執行計劃走的是對索引IDX_EMP_2的索引跳躍式掃描,對應“INDEXSKIP SCAN”。
3、與表連接相關的執行計劃
Oracle數據庫里常見的與表連接相關的一些方法:排序合并連接、嵌套循環連接、哈希連接等以及反連接和半連接
zx@MYDB>create table t1(col1 number,col2 varchar2(1)); Table created. zx@MYDB>create table t2(col2 varchar2(1),col3 varchar2(2)); Table created. zx@MYDB>insert into t1 values(1,'A'); 1 row created. zx@MYDB>insert into t1 values(2,'B'); 1 row created. zx@MYDB>insert into t1 values(3,'C'); 1 row created. zx@MYDB>insert into t1 values(4,'D'); 1 row created. zx@MYDB>insert into t1 values(5,'E'); 1 row created. zx@MYDB>insert into t2 values('A','A2'); 1 row created. zx@MYDB>insert into t2 values('B','B2'); 1 row created. zx@MYDB>insert into t2 values('D','D2'); 1 row created. zx@MYDB>insert into t2 values('E','E2'); 1 row created. zx@MYDB> zx@MYDB>commit; Commit complete. zx@MYDB>select * from t1; COL1 COL ---------- --- 1 A 2 B 3 C 4 D 5 E zx@MYDB>select * from t2; COL COL3 --- ------ A A2 B B2 D D2 E E2 zx@MYDB>select t1.col1,t1.col2,t2.col3 from t1,t2 where t1.col2=t2.col2; COL1 COL COL3 ---------- --- ------ 1 A A2 2 B B2 4 D D2 5 E E2
從上面的輸出可以看出,SQL的執行計劃走的是對表T1和T2的哈希連接,連接條件是t1.col2=t2.col2,對應的關鍵字是“HASH JOIN”。
使用強制走排序合并連接的Hint后再次執行SQL
zx@MYDB>select /*+use_merge(t1,t2) */t1.col1,t1.col2,t2.col3 from t1,t2 where t1.col2=t2.col2; COL1 COL COL3 ---------- --- ------ 1 A A2 2 B B2 4 D D2 5 E E2
從上面的輸出可以看出現在SQL的執行計劃走的是對表T1和T2的排序合并連接,對應的關鍵字是“MERGEJOIN”和“SORT JOIN”。
接著使用強制走嵌套循環連接的Hint后再次執行SQL
zx@MYDB>select /*+use_nl(t1,t2) */t1.col1,t1.col2,t2.col3 from t1,t2 where t1.col2=t2.col2; COL1 COL COL3 ---------- --- ------ 1 A A2 2 B B2 4 D D2 5 E E2
從上面的輸出可以看出現在SQL的執行計劃走的是對表T1和T2的嵌套循環連接,對應的關鍵字是“NESTEDLOOPS”
嵌套循環連接的驅動表是可以變的,我們使用Hint將上述SQL的驅動表改為T1再將執行SQL
zx@MYDB>select /*+ ordered use_nl(t1,t2) */t1.col1,t1.col2,t2.col3 from t1,t2 where t1.col2=t2.col2; COL1 COL COL3 ---------- --- ------ 1 A A2 2 B B2 4 D D2 5 E E2
從結果中可以看到,嵌套循環連接的驅動表確實已經變為T1
再看反連接的例子。首先將表T1和T2的連接列col2改為NOT NULL,以便能走出我們想要的反連接的執行計劃
zx@MYDB>alter table t1 modify col2 not null; Table altered. zx@MYDB>alter table t2 modify col2 not null; Table altered. zx@MYDB>select * from t1 where col2 not in (select col2 from t2 where col3='A2'); COL1 COL ---------- --- 5 E 4 D 2 B 3 C
從輸出內容上可以看出,SQL的執行計劃走的是對表T1和T2的哈希反連接,反連接在執行計劃中對應的關鍵字是“ANTI”,哈希反連接對應的就是“HASH JOIN ANTI”。
反連接的具體連接方法是可變的,這里使用Hint將SQL的反連接改為排序合并反連接
zx@MYDB>select * from t1 where col2 not in (select /*+ MERGE_AJ */ col2 from t2 where col3='A2'); COL1 COL ---------- --- 2 B 3 C 4 D 5 E
從輸出內容可以看出,SQL的執行計劃走的是對表T1和T2的排序合并反連接,對應的關鍵字是“MERGE JOIN ANTI”。
再使用Hint將SQL的反連接方法改為嵌套循環反連接
zx@MYDB>select * from t1 where col2 not in (select /*+ NL_AJ */ col2 from t2 where col3='A2'); COL1 COL ---------- --- 2 B 3 C 4 D 5 E
再看半連接的例子。
zx@MYDB>insert into t2 values('E','E3'); 1 row created. zx@MYDB>commit; Commit complete. zx@MYDB>select * from t1 where exists(select * from t2 where t1.col2=t2.col2 and col3>'D2'); COL1 COL ---------- --- 5 E
從輸出可以看出,SQL的執行計劃走的是對表T1和T2的哈希半連接,半連接在執行計劃中對應的關鍵字是“SEMI”,哈希半連接在執行計劃中對應的關鍵字是“HASH JOIN SEMI”。
半連接的具體連接方法是可變的,使用Hint將SQL的半連接方法改為排序合并半連接:
zx@MYDB>select * from t1 where exists(select /*+ MERGE_SJ */* from t2 where t1.col2=t2.col2 and col3>'D2'); COL1 COL ---------- --- 5 E
從輸出內容可以看出,SQL的執行計劃走的是對表T1和T2的排序合并半連接,對應的關鍵字是“MERGE JOIN SEMI”。
再使用Hint把SQL的半連接方法改為嵌套循環半連接:
zx@MYDB>select * from t1 where exists(select /*+ NL_SJ */* from t2 where t1.col2=t2.col2 and col3>'D2'); COL1 COL ---------- --- 5 E
從輸出內容可以看出,SQL的執行計劃走的是對表T1和T2的嵌套循環半連接,對應的關鍵字是“NESTED LOOPS SEMI”
4、關于位圖索引相關的執行計劃
Oracle數據庫里常見的與位圖索引訪問相關的方法包括如下這些類型:位圖索引單鍵值掃描、位圖索引范圍掃描、位圖索引全掃描、位圖索引快速全掃描、位圖按位與、位圖按位或、位圖按位減等。
Oracle在使用完位圖索引后通常會將最后的位圖運算結果轉化為ROWID,這一步轉換過程對應的執行計劃中的“BITMAP CONVERSION TO ROWIDS”。
zx@MYDB>create table customer 2 ( 3 customer# number, 4 marital_status varchar2(10), 5 region varchar2(10), 6 gender varchar2(10), 7 income_level varchar2(10) 8 ); Table created. zx@MYDB>insert into customer values(101,'single','east','male','bracket_1'); 1 row created. zx@MYDB>insert into customer values(102,'married','central','female','bracket_4'); 1 row created. zx@MYDB>insert into customer values(103,'married','west','female','bracket_2'); 1 row created. zx@MYDB>insert into customer values(104,'divorced','west','male','bracket_4'); 1 row created. zx@MYDB>insert into customer values(105,'single','central','female','bracket_2'); 1 row created. zx@MYDB>insert into customer values(106,'married','central','female','bracket_3'); 1 row created. zx@MYDB>commit; Commit complete. zx@MYDB>create bitmap index idx_b_region on customer(region); Index created. zx@MYDB>create bitmap index idx_b_maritalstatus on customer(marital_status); Index created. zx@MYDB>exec dbms_stats.gather_table_stats(ownname=>USER,tabname=>'CUSTOMER',estimate_percent=>100,cascade=>true); PL/SQL procedure successfully completed. zx@MYDB>select /*+ index(customer idx_b_region) */ customer# from customer where region='east'; CUSTOMER# ---------- 101
從上面的輸出內容可以看出,SQL的執行計劃走的是對位圖索引IDX_B_REGION的位圖索引單鍵值掃描,對就的關鍵字是“BITMAP INDEX SINGLE VALUE”。
把SQL改寫為范圍查詢后再次執行
zx@MYDB>select /*+index(customer idx_b_region) */ customer# from customer where region between 'east' and 'west'; CUSTOMER# ---------- 101 103 104
從輸出內容可以看出SQL走的執行計劃是對位圖索引IDX_B_REGION的位圖索引范圍掃描,對應的關鍵字是“BITMAP INDEX RANGE SCAN”。
去掉where條件,并且只查詢位圖索引IDX_B_REGION的索引鍵值列:
zx@MYDB>select region from customer; REGION ------------------------------ central central central east west west
從輸出可以看出SQL走的執行計劃是對位圖索引IDX_B_REGION的位圖索引快速全掃描,對應的關鍵字是“BIT INDEX FAST FULL SCAN”。
執行如下SQL:
zx@MYDB>select count(*) from customer where marital_status='married' and region in ('central','west'); COUNT(*) ---------- 3
從輸出內容可以看出SQL走的執行計劃中,用到了位圖按位與操作,對應的關鍵字是“BITMAP AND”和位圖按位或操作,對應的關鍵字是“BITMAP OR”。
再構造位圖按位減的執行計劃,SQL如下:
zx@MYDB>select /*+index(customer idx_b_maritalstatus) index(customer idx_b_region) */ customer# from customer where marital_status='married' and region!='central'; CUSTOMER# ---------- 103
從輸出的執行計劃中,位圖按位減的執行計劃對應的關鍵字是“BITMAP MINUX”。
參考《基于Oracle的SQL優化》
當前題目:Oracle里常見的執行計劃
地址分享:http://m.kartarina.com/article22/pihicc.html
成都網站建設公司_創新互聯,為您提供網站改版、網站制作、網站導航、服務器托管、外貿建站、網站設計公司
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯