一. 測試環境
SQL> select * from v$version where rownum=1;
BANNER
Oracle Database 11g Enterprise Edition Release11.2.0.3.0 - 64bit Production
SQL> create table dave as selectobject_id,object_name,object_type,created,timestamp,status from all_objects;
表已創建。
SQL> create table dave2 as select * from dave;
表已創建。
–收集統計信息,這裏沒有收集直方圖:
SQL> exec dbms_stats.gather_table_stats(ownname=>‘SYS’,tabname =>‘DAVE’,estimate_percent => 10 ,method_opt =>‘FORCOLUMNS size 1’,degree=>10,cascade => true);
PL/SQL 過程已成功完成。
SQL> exec dbms_stats.gather_table_stats(ownname=>‘SYS’,tabname =>‘DAVE2’,estimate_percent => 10 ,method_opt =>‘FORCOLUMNS size 1’,degree=>10,cascade => true);
PL/SQL 過程已成功完成。
–避免其他影響,先刷新buffer cache:
SQL> alter system flush buffer_cache;
系統已更改。
–查看全表掃描時的執行計劃:
SQL> set autot traceonly
SQL> select d1.object_name,d2.object_type fromdave d1,dave2 d2 where d1.object_id=d2.object_id;
已選擇72762行。
執行計劃
Plan hash value: 3613449503
| Id |Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
| 0 |SELECT STATEMENT | | 72520 | 3824K| | 695 (1)| 00:00:09 |
|* 1 | HASH JOIN | | 72520 | 3824K| 2536K| 695 (1)| 00:00:09 |
| 2 | TABLE ACCESS FULL| DAVE2 | 71990 | 1687K| | 213 (1)| 00:00:03 |
| 3 | TABLE ACCESS FULL| DAVE | 72520 | 2124K| | 213 (1)| 00:00:03 |
Predicate Information (identified by operation id):
1 -access(“D1”.“OBJECT_ID”=“D2”.“OBJECT_ID”)
統計信息
0 recursive calls
0 db block gets
6353 consistent gets
1558 physical reads
0 redo size
3388939 bytes sent via SQL*Net toclient
53874 bytes received via SQL*Netfrom client
4852 SQL*Net roundtrips to/fromclient
0 sorts (memory)
0 sorts (disk)
72762 rows processed
–這裏產生了1558的物理讀
SQL>
–在object_id上創建索引:
SQL> create index idx_dave_object_idon dave(object_id);
索引已創建。
SQL> create index idx_dave_object_id2 ondave2(object_id);
索引已創建。
–在次查看執行計劃:
SQL> select d1.object_name,d2.object_type fromdave d1,dave2 d2 where d1.object_id=d2.object_id;
已選擇72762行。
執行計劃
Plan hash value: 3613449503
| Id |Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
| 0 |SELECT STATEMENT | | 72520 | 3824K| | 695 (1)| 00:00:09 |
|* 1 | HASH JOIN | | 72520 | 3824K| 2536K| 695 (1)| 00:00:09 |
| 2 | TABLE ACCESS FULL| DAVE2 | 71990 | 1687K| | 213 (1)| 00:00:03 |
| 3 | TABLE ACCESS FULL| DAVE | 72520 | 2124K| | 213 (1)| 00:00:03 |
Predicate Information (identified by operation id):
1 -access(“D1”.“OBJECT_ID”=“D2”.“OBJECT_ID”)
統計信息
1 recursive calls
0 db block gets
6353 consistent gets
0 physical reads
0 redo size
3388939 bytes sent via SQL*Net toclient
53874 bytes received via SQL*Netfrom client
4852 SQL*Net roundtrips to/fromclient
0 sorts (memory)
0 sorts (disk)
72762 rows processed
這裏的物理讀爲0. 但是還是走的是全表掃描。
–刷新一下buffer,增加索引條件:
SQL> alter system flush buffer_cache;
系統已更改。
SQL> select d1.object_name,d2.object_type fromdave d1,dave2 d2 where d1.object_id=d2.object_id and d1.object_id <100;
已選擇98行。
執行計劃
Plan hash value: 504164237
| Id |Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 |SELECT STATEMENT | | 3600 | 189K| 23 (5)| 00:00:01 |
|* 1 | HASH JOIN | | 3600 | 189K| 23 (5)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| DAVE2 | 3600 | 86400 | 11 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_DAVE_OBJECT_ID2 | 648 | | 3 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| DAVE | 3626 | 106K| 11 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IDX_DAVE_OBJECT_ID | 653| | 3 (0)| 00:00:01 |
Predicate Information (identified by operation id):
1 -access(“D1”.“OBJECT_ID”=“D2”.“OBJECT_ID”)
3 -access(“D2”.“OBJECT_ID”<100)
5 -access(“D1”.“OBJECT_ID”<100)
統計信息
1 recursive calls
0 db block gets
20 consistent gets
6 physical reads
0 redo size
3317 bytes sent via SQL*Net toclient
590 bytes received via SQL*Netfrom client
8 SQL*Net roundtrips to/fromclient
0 sorts (memory)
0 sorts (disk)
98 rows processed
SQL>
走索引之後,物理讀從1558降到6.
二.說明
在上面的測試中,我們看到了索引掃描的類型和多表關聯的類型,關於這幾種類型的說明,參考:
Oracle 索引掃描的五種類型
http://blog.csdn.net/tianlesoftware/article/details/5852106
多表連接的三種方式詳解 HASH JOIN MERGE JOINNESTED LOOP
http://blog.csdn.net/tianlesoftware/article/details/5826546
從執行計劃中,當我們走索引之後,在對應的表上就會出現:
TABLE ACCESS BY INDEX ROWID
在如下文章中對OracleROWID 有說明。
Oracle Rowid 介紹
http://blog.csdn.net/tianlesoftware/article/details/5020718
rowid是僞列(pseudocolumn),在查詢結果輸出時它被構造出來的。rowid並不會真正存在於表的data block中,其存在於index當中,用來通過rowid來尋找表中的行數據。
ROWID 由以下幾部分組成:
-
數據對象編號:每個數據對象(如表或索引)在創建時都分配有此編號,並且此編號在數據庫中是唯一的
-
相關文件編號:此編號對於表空間中的每個數據文件是唯一的
-
塊編號:表示包含此行的塊在數據文件中的位置
-
行編號:標識塊頭中行目錄位置的位置
Oracle 索引中保存的是我們字段的值和該值對應的rowid,我們根據索引進行查找時,就會返回該block的rowid,然後根據rowid直接去block上去我們需要的數據,因此就出現了:
TABLE ACCESS BY INDEX ROWID
因爲ROWID 對應一個block,所以當使用TABLE ACCESS BY INDEX ROWID時,每次就只能讀取一個block。
假設我們我們的數據返回100個ROWID,其中10個row 位於同一個block上,那麼我們只需要訪問91次block,就可以拿到我們需要的數據。
關於如何確定row記錄在哪個block的方法參考:
Oracle rdba和 dba 說明
http://blog.csdn.net/tianlesoftware/article/details/6529346
小結:
(1) TABLE ACCESS BY INDEX ROWID 只出現在使用索引的情況下。
(2) TABLE ACCESS BY INDEX ROWID 是單塊讀,每次只能讀取一個block