oracle數據庫索引相關知識

1、 索引:

按腳本新建兩張表T1,T2表中數據存儲以下: java

wps754.tmp.jpeg

當對兩張表進行相同的查詢操做時,T1表會使用索引,但T2表不會它將進行全表掃描。sql

select * from t1 where id=10;
select * from t2 where id=10;
複製代碼

這主要是由於T1表的聚簇因子的值接近表的表塊數量,而 T2表的聚簇因子的值接近表中數據數量。 若是聚簇因子的值接近表的表塊數量,則說明目標索引索引行和存儲於對應表中數據行的存儲順序類似程度很是高。這就意味着Oracle走索引範圍掃描後取得目標rowid再回表訪問對應表塊中的數據時,相鄰的索引行所對應的rowid找到的表數據很可能處於同一表塊中。即Oracle在經過索引行記錄的rowid回表第一次讀取對應的表塊並將該表塊緩存在buffer cache中後,當再經過相鄰索引行記錄的rowid回表第二次去讀取對應的表塊時就不須要再產生物理I/O了,由於這兩次訪問的是同一個表塊,而這個表塊已經緩存在buffer cache中了。 若是聚簇因子的值接近表的記錄數,則說明目標索引索引行和存儲於對應表中數據行的存儲順序類似程度很是低,這就意味着Oracle走索引範圍掃描取得rowid再回表訪問對應表塊的數據時,相鄰的索引行所對應的rowid很可能不處於同一表塊中,說明Oracle每次都要讀對應的表塊,每次都產生物理I/O。數據庫

B-TREE索引結構

wps764.tmp.jpeg

經過B樹索引訪問數據的過程:先訪問相關的B樹索引,而後根據訪問該索引後獲得的ROWID再回表訪問相應的數據行記錄。 這裏訪問相關的B樹索引和回表都須要消耗I/O,這意味着訪問索引的成本由兩部分組成:一部分是訪問相關的B樹索引的成本(從根節點定位到相關的分支塊,再定位到相關的葉子塊,最後對這些葉子塊進行掃描);另外一部分是回表的成本(根據ROWID再回表掃面對應的數據行所在數據塊)緩存

wps765.tmp.jpeg

B樹索引的優點:

  • 全部的索引葉子塊都在同一層,即它們距離索引根節點的深度是相同的。這也意味着訪問索引葉子塊的任何一個索引鍵值所花費的時間幾乎相同;
  • 經過B樹索引訪問表裏行記錄的效率並不會隨着表的數據量的遞增而顯著下降,即經過走索引訪問數據的時間是可控的、基本穩定的。

2、 訪問索引的方法:

1.索引惟一性掃描,即準對惟一索引的掃面。

wps766.tmp.jpeg

2.索引範圍掃描,當掃描的對象時惟一性索引時,where條件必定是範圍查詢(即detween、<、>等);非惟一索引時沒有限制。

wps777.tmp.jpeg

3.索引全掃描:

適用於全部類型的B樹索引。所謂的索引全掃描是指掃描目標索引的全部葉子塊的全部索引列。這裏須要注意索引全掃描須要掃描全部的葉子塊但不意味着須要掃描全部的分支塊。Oracle在作索引全掃描時只須要經過訪問必要的分支塊定位到位於該索引最左邊的葉子塊的第一行索引行就能夠利用該索引葉子塊之間的雙向指針鏈表,從左到右依次順序掃描該索引全部葉子塊的全部索引行了。 由於索引是有序的因此經過索引全掃描獲得的結果也是有序的便可以免排序操做。 函數

wps778.tmp.jpeg

由於索引全掃描獲得的結果是有序因此不能並行執行,並且只能單塊讀。oop

注意:索引列不能容許null,不然將不會走索引全掃描。優化

4.索引快速全掃描:和索引全掃描相似但它可使用多塊讀,也能夠並行執行,但它的結果是無序的。

5.全表掃描可使用多塊讀,會一直掃描到高水位。

wps788.tmp.jpeg

須要注意當使用delete刪除數據時,高水位會保持不變就是說全表掃描所需的時間會差很少。 ui

wps78A.tmp.jpeg

3、 索引的實際開發中的使用

  1. 查詢列的值爲null的狀況下如何走索引,能夠參考建立以下的索引:
create index idx_t3 on test3(object_id,0);
複製代碼
  1. 函數索引:
create index idx_t3_tm on test3(trunc(created));
複製代碼
  1. 左%使用索引:
create index idx_t3_nm on test3(reverse(object_name));
複製代碼
  1. 兩列拼後接走索引:
create index idx_t3_un on test3(owner||'_'||object_type);
複製代碼
  1. 組合索引:選擇性高的字段放前面
create index idx_t3 on test3(object_name,object_id);
select t.* from edcs.test3 t where t.object_id > 1 and t.object_name='I_IND1';
複製代碼

4、 錶鏈接方法

1. 排序合併連接(Sort Merge Join)

若是兩張表(T1和T2表)在作表連接時使用的是排序合併連接,則Oarcle會按下面的步驟執行:spa

  1. 首先以目標表SQL中指定的謂語條件(若是有的話)去訪問表T1,而後對訪問結果按照表T1中的連接列作排序,排序後獲得的結果集記爲1;
  2. 接着以目標表SQL中指定的謂語條件(若是有的話)去訪問表T2,而後對訪問結果按照表T2中的連接列作排序,排序後獲得的結果集記爲2;
  3. 最後對結果集1和結果集1作合併操做,從中取出匹配的記錄返回。

一般狀況下,排序合併連接的效率不如哈希連接,但哈希連接只能使用等值連接條件,而排序合併連接能夠用於非等值連接。排序合併連接由於須要對數據進行排序當數據量很大時排序是很耗I/O的。3d

可使用use_merge()提示指定走排序合併連接。

wps79B.tmp.jpeg

2. 嵌套循環連接(Nested Loops Join)

若是兩張表(T1和T2表)在作表連接時使用的是嵌套循環連接,則Oarcle會按下面的步驟執行:

  1. 首先優化器會按必定的規則來決定表T1 、T2中誰是驅動表、誰是被驅動表。驅動表用於外層循環,被驅動表用於內層循環。這裏假設T1是驅動表,T2是被驅動表;
  2. 接着以目標SQL中指定的謂語條件(若是有的話)去訪問驅動表T1獲得的結果集記爲結果集1;
  3. 而後遍歷結果集1逐條取出,在遍歷被驅動表T2找到匹配的記錄。至關於java中的兩層for循環;

經過上面的描述咱們能夠看出,驅動表的結果集每每比較小,若是在被驅動表的連接上又存在選擇性較好的索引,那麼嵌套循環執行效率就會很是高。 嵌套循環還有一個優點就是它能夠實現快速響應,即它能夠第一時間將已連接過且符合連接條件的數據先返回,而不用等全部的連接操做都完成在返回。 可使用use_nl()提示指定走嵌套循環連接。

wps79C.tmp.jpeg

三、 哈希連接(Hash Join)

哈希連接是一種兩個表在作表連接時主要依靠哈希運算來獲得連接結果集的表連接方法。

在Oracle7.3以前數據庫經常使用的錶鏈接方法主要是排序合併連接和嵌套循環連接兩種,但這兩種方法都有各自明顯的缺陷。對於排序合併連接,若是兩個表在施加了目標SQL中指定的謂語條件後獲得的結果集很大且須要排序則排序合併連接的執行效率必定不高;對於嵌套循環連接若是驅動表對應的驅動結果集很大,即便在被驅動表的連接列上存在索引效率也是很低。爲解決上面的問題在Oracle7.3之後引入了哈希連接。但它只適用於等值的連接條件。

可使用use_hash()提示指定走哈希連接。

wps79D.tmp.jpeg

4. 笛卡爾連接(Cross Join)

若是兩張表(T1和T2表)在作表連接時使用的是笛卡爾連接,則Oarcle會按下面的步驟執行:

  1. 首先以目標表SQL中指定的謂語條件(若是有的話)去訪問表T1,此時獲得的結果集記爲結果集1記錄數爲m;
  2. 接着以目標表SQL中指定的謂語條件(若是有的話)去訪問表T2,此時獲得的結果集記爲結果集2記錄數爲n;
  3. 最終對結果集1和結果集2執行合併操做,最終的記錄數爲m*n。

sql優化腳本

--建表
create table test1 as (select trunc((rownum-1)/100) id,rpad(rownum,100) t_pad from dba_source where rownum<=10000);
create table test2 as (select mod(rownum-1,100) id,rpad(rownum,100) t_pad from dba_source where rownum<=10000);
--建索引
create index idx_t1 on test1(id);
create index idx_t2 on test2(id);
--表分析的方法
analyze table test1 compute statistics for table ;
analyze table test2 compute statistics for table ;
--索引分析的方法
analyze index idx_t1 compute statistics ;
analyze index idx_t2 compute statistics ;

--查看統計信息
SELECT A.INDEX_NAME, B.NUM_ROWS, B.BLOCKS, A.CLUSTERING_FACTOR
  FROM USER_INDEXES A, USER_TABLES B
 WHERE A.INDEX_NAME IN ('IDX_T1','IDX_T2')
   AND A.TABLE_NAME = B.TABLE_NAME;
   
select count(1) from test1 where id=10;
select count(1) from test2 where id=10; 

select count(1) from test1 ;
select count(1) from test2 ;
  
select * from edcs.test1 where id=10;
select * from edcs.test2 t where id=10;  


create table test3 as select * from dba_objects;
analyze table edcs.test3 compute statistics for table ;
create index idx_t3 on test3(object_id);
analyze index idx_t3 compute statistics;
create index idx_t3_tm on test3(created);
analyze index idx_t3_tm compute statistics ;
select count(1) from test3 t;  --77230

--查看執行計劃
F5
set autot trace;
select * from table(dbms_xplan.display_cursor(null,null,'advanced'));

--惟一索引
select * from tm_dcs_process_detail_config t where t.process_detail_id=2585378;
--範圍索引
select * from edcs.test3 t where t.object_id=15;
--索引全掃描
select t.process_detail_id from tm_dcs_process_detail_config t ;
--索引快速全掃描
select T.OBJECT_ID from TEST3 T;
select /*+ index_ffs(T IDX_T3) */ T.OBJECT_ID from TEST3 T;


--索引開發舉例
--存在null值走索引的方法
select rowid ,t.* from test3 t where t.object_id is null;
drop index idx_t3;
create index idx_t3 on test3(object_id,0);
analyze index idx_t3 compute statistics ;

--函數索引
select * from test3 t where trunc(created)>=date'2011-09-16';
drop index idx_t3_tm;
create index idx_t3_tm on test3(trunc(created));
analyze index idx_t3_tm compute statistics ;
--%使用索引
create index idx_t3_nm on test3(object_name);
select * from test3 t where t.object_name like '%CON1';
drop index idx_t3_nm;
create index idx_t3_nm on test3(reverse(object_name));
analyze index idx_t3_nm compute statistics ;
select t.* from test3 t where reverse(t.object_name) LIKE reverse('%CON1'); 
--兩列拼接走索引
select * from test3 t where owner||'_'||object_type ='SYS_CLUSTER';
create index idx_t3_un on test3(owner||'_'||object_type);
analyze index idx_t3_un compute statistics ;
--組合索引
create index idx_t3 on test3(object_id,object_name);
create index idx_t3 on test3(object_name,object_id);
select t.* from edcs.test3 t where t.object_id > 1 and t.object_name='I_IND1';

--錶鏈接方式
select * from tt_waybill_bak t1,tt_waybill_fee_bak t2 where t1.waybill_no=t2.waybill_no;

select /*+ use_merge(t1 t2) */ * from tt_waybill_bak t1,tt_waybill_fee_bak t2 where t1.waybill_no=t2.waybill_no;
select /*+ use_nl(t1 t2) */ * from tt_waybill_bak t1,tt_waybill_fee_bak t2 where t1.waybill_no=t2.waybill_no;
select /*+ use_hash(t1 t2) */ * from tt_waybill_bak t1,tt_waybill_fee_bak t2 where t1.waybill_no=t2.waybill_no;


select t1.*
  from tt_waybill_fee_bak t1
 where t1.waybill_no in
       (select t2.waybill_no from temp3 t2);
       
select t1.*       
  from tt_waybill_fee_bak t1,temp3 t2
 where t1.waybill_no = t2.waybill_no;      
       
select t1.*
  from tt_waybill_fee_bak t1
 where t1.waybill_no in
       (select /*+ no_unnest */ t2.waybill_no from temp3 t2);  

--並行 
select /*+ PARALLEL(T1 4)*/ t1.*
  from tt_waybill_fee_bak t1
 where t1.waybill_no in
       (select t2.waybill_no from temp3 t2);
       
select  t1.*
  from tt_waybill_fee_bak t1
 where t1.waybill_no in
       (select t2.waybill_no from temp3 t2);
複製代碼

轉載自: 簡書 - 低至一折起

文章:www.jianshu.com/p/45b1b8838…

相關文章
相關標籤/搜索