oracle索引與like條件的關係(轉)

http://hi.baidu.com/yinghangbin/blog/item/5e1194cf648cbd1c93457e1b.html

oracle索引與like條件的關係

oracle索引與like條件的關係

表:gzl_action_define

字段:id:主鍵,有索引
name:一般字段,無索引
以下是各種寫法的結果

1.使用索引(UNIQUE SCAN)

select t.name from gzl_action_define t where t.id = '12'

2.使用索引(RANGE SCAN)

select t.name from gzl_action_define t where t.id like '12%'

3.不使用索引(TABLE ACCESS (FULL))

select t.name from gzl_action_define t where t.id like '%12'

4.使用索引(FAST FULL SCAN)

select t.id from gzl_action_define t where t.id like '%12'

5.不使用索引(TABLE ACCESS (FULL))

select t.name from gzl_action_define t where t.id like '%12%'

6.使用索引(FAST FULL SCAN)

select t.id from gzl_action_define t where t.id like '%12%'

------------------------------------------------------------------------------------------
 
 Oracle 索引相關理論知識
*聲明:以下內容,來源於網絡資源*

 

*(一)  * *建立索引常用的規則如下:*

1)         表的主鍵、外鍵必須有索引;

2)         數據量超過300的表應該有索引;

3)         經常與其他表進行連接的表,在連接字段上應該建立索引;

4)         經常出現在Where子句中的字段,特別是大表的字段,應該建立索引;

5)         索引應該建在選擇性高的字段上;

6)         索引應該建在小字段上,對於大的文本字段甚至超長字段,不要建索引;

7)         複合索引的建立需要進行仔細分析;儘量考慮用單字段索引代替:

a)         正確選擇複合索引中的主列字段,一般是選擇性較好的字段;

b)         複合索引的幾個字段是否經常同時以AND方式出現在Where
子句中?單字段查詢是否極少甚至沒有?如果是,則可以建立複合索引;否則考慮單字段索引;

c)         如果複合索引中包含的字段經常單獨出現在Where子句中,則分解爲多個單字段索引;

d)         如果複合索引所包含的字段超過3個,那麼仔細考慮其必要性,考慮減少複合的字段;

e)         如果既有單字段索引,又有這幾個字段上的複合索引,一般可以刪除複合索引;

8)         頻繁進行數據操作的表,不要建立太多的索引;

*9)        * *刪除無用的索引,避免對執行計劃造成負面影響;*

以上是一些普遍的建立索引時的判斷依據。一言以蔽之,索引的建立必須慎重,對每個索引的必要性都應該經過仔細分析,要有建立的依據。因爲太多的索引與不充分、不正確的索引對性能都毫無益處:在表上建立的每個索引都會增加存儲開銷,索引對於插入、刪除、更新操作也會增加處理上的開銷。
另外,過多的複合索引,在有單字段索引的情況下,一般都是沒有存在價值的;相反,還會降低數據增加刪除時的性能,特別是對頻繁更新的表來說,負面影響更大。

*(二)  * *避免對列的操作:*

1)         任何對列的操作都可能導致全表掃描,這裏所謂的操作包括數據庫函數、計算表達式等等,查詢時要*
儘可能將操作移至等式的右邊,甚至去掉函數。*

例1:下列SQL條件語句中的列都建有恰當的索引,但30萬行數據情況下執行速度卻非常慢:

select * from record where  substrb(CardNo,1,4)='5378'(13秒)

select * from record where  amount/30< 1000(11秒)

select * from record where  to_char(ActionTime,'yyyymmdd')='19991201'(10秒)

由於where子句中對列的任何操作結果都是在SQL
運行時逐行計算得到的,因此它不得不進行表掃描,而沒有使用該列上面的索引;如果這些結果在查詢編譯時就能得到,那麼就可以被SQL
優化器優化,使用索引,避免表掃描,因此將SQL重寫如下:

select * from record where CardNo like  '5378%'(< 1秒)

select * from record where amount  < 1000*30(< 1秒)

select * from record where ActionTime= to_date ('19991201' ,'yyyymmdd')(< 1秒)

(三)   *避免不必要的類型轉換:*

1)         儘量避免潛在的數據類型轉換。如將字符型數據與數值型數據比較,ORACLE會自動將字符型用to_number()
函數進行轉換,從而導致全表掃描。

例2:表tab1中的列col1是字符型(char),則以下語句存在類型轉換:

select col1,col2 from tab1 where col1>10;

應該寫爲: select col1,col2 from tab1 where col1>'10'

*(四)  * *增加查詢的範圍限制*

1)         增加查詢的範圍限制,避免全範圍的搜索

例3:以下查詢表record 中時間ActionTime小於2001年3月1日的數據

select * from record where ActionTime < to_date ('20010301' ,'yyyymm')

查詢計劃表明,上面的查詢對錶進行全表掃描,如果我們知道表中的最早的數據爲2001年1月1日,那麼,可以增加一個最小時間,使查詢在一個完整的範圍之內:

select * from record where ActionTime < to_date ('20010301' ,'yyyymm') and  
 ActionTime > to_date ('20010101' ,'yyyymm')

後一種SQL語句將利用上ActionTime字段上的索引,從而提高查詢效率。把'20010301'
換成一個變量,根據取值的機率,可以有一半以上的機會提高效率。同理,對於大於某個值的查詢,如果知道當前可能的最大值,也可以在Where子句中加上 “AND
列名< MAX(最大值)”。

*(五)  * *儘量去掉"IN"**、"OR"*

*1)        * 含有"IN"、"OR"的Where
子句常會使用工作表,使索引失效;如果不產生大量重複值,可以考慮把子句拆開;拆開的子句中應該包含索引。

例4:select count(*) from stuff where id_no in('0','1')(23秒)

可以考慮將or子句分開:

select count(*) from stuff where id_no='0'  

select count(*) from stuff where id_no='1'

然後再做一個簡單的加法,與原來的SQL語句相比,查詢速度更快

*(六)  * *儘量去掉 "<>"*

*1)        * 儘量去掉 "<>",避免全表掃描,如果數據是枚舉值,且取值範圍固定,則修改爲"OR"方式

例5:UPDATE SERVICEINFO SET STATE=0 WHERE STATE<>0;

以上語句由於其中包含了"<>",執行計劃中用了全表掃描(TABLE ACCESS FULL),沒有用到state
字段上的索引。實際應用中,由於業務邏輯的限制,字段state爲枚舉值,只能等於0,1或2,而且,值等於=1,2的很少,因此可以去掉"<>"
,利用索引來提高效率。

修改爲:UPDATE SERVICEINFO SET STATE=0  WHERE STATE = 1 OR STATE = 2
。進一步的修改可以參考第4種方法

*(七)  * *去掉Where**子句中的IS NULL**和IS NOT NULL*

*1)        * Where字句中的IS NULL和IS NOT NULL
將不會使用索引而是進行全表搜索,因此需要通過改變查詢方式,分情況討論等方法,去掉Where子句中的IS NULL和IS NOT NULL。

*(八)  * *索引提高數據分佈不均勻時查詢效率*

1)         索引的選擇性低,但數據的值分佈差異很大時,仍然可以利用索引提高效率。

A、數據分佈不均勻的特殊情況下,選擇性不高的索引也要創建。

表ServiceInfo中數據量很大,假設有一百萬行,其中有一個字段DisposalCourseFlag,取值範圍爲枚舉值:[0,1,2,3,4,5,6
,7]。按照前面說的索引建立的規則,“選擇性不高的字段不應該建立索引,該字段只有8
種取值,索引值的重複率很高,索引選擇性明顯很低,因此不建索引。然而,由於該字段上數據值的分佈情況非常特殊,具體如下表:

取值範圍                   1~5        6        7
佔總數據量的百分比        1%        98%        1%

而且,常用的查詢中,查詢DisposalCourseFlag<6
的情況既多又頻繁,毫無疑問,如果能夠建立索引,並且被應用,那麼將大大提高這種情況的查詢效率。因此,我們需要在該字段上建立索引。

*(九)  * *like**子句儘量前端匹配*

*1)        * 因爲like參數使用的非常頻繁,因此如果能夠對like子句使用索引,將很高的提高查詢的效率。

例6:select * from city where name like ‘%S%’

以上查詢的執行計劃用了全表掃描(TABLE ACCESS FULL),如果能夠修改爲:

select * from city where name like ‘S%’

那麼查詢的執行計劃將會變成(INDEX RANGE SCAN),成功的利用了name字段的索引。這意味着Oracle SQL優化器會識別出用於索引的
like子句,只要該查詢的匹配端是具體值。因此我們在做like查詢時,應該儘量使查詢的匹配端是具體值,即使用like ‘S%’。

*(十)  * *用Case**語句合併多重掃描*

1)         我們常常必須基於多組數據表計算不同的聚集。例如下例通過三個獨立查詢:

例8:1)select count(*) from emp where sal<1000;

2)select count(*) from emp where sal between 1000 and 5000;

3)select count(*) from emp where sal>5000;

這樣我們需要進行三次全表查詢,但是如果我們使用case語句:

select
count (sale when sal <1000
then 1 else null end)                count_poor,
count (sale when between 1000 and 5000
then 1 else null end)                count_blue_collar,
count (sale when sal >5000
then 1 else null end)                count_poor
from emp;
這樣查詢的結果一樣,但是執行計劃只進行了一次全表查詢。

*(十一)        * *使用基於函數的索引*

1)         select * from emp where substr(ename,1,2)=’SM’;

可以創建一個帶有substr函數的基於函數的索引:

create index emp_ename_substr on eemp ( substr(ename,1,2) );

這樣在執行上面的查詢語句時,這個基於函數的索引將排上用場,執行計劃將是(INDEX RANGE SCAN)。

*(十二)        * *基於函數的索引要求等式匹配*

1)         創建了基於函數的索引,但是如果執行下面的查詢:

select * from emp where substr(ename,1,1)=’S’

得到的執行計劃將還是(TABLE ACCESS FULL
),因爲只有當數據列能夠等式匹配時,基於函數的索引才能生效,這樣對於這種索引的計劃和維護的要求都很高。請注意,向表中添加索引是非常危險的操作,因爲這將導致許多查詢執行計劃的變更。然而,如果我們使用基於函數的索引就不會產生這樣的問題,因爲
Oracle只有在查詢使用了匹配的內置函數時纔會使用這種類型的索引。

*(十三)        * *決定使用全表掃描還是使用索引*

1)         
在大多數情況下,全表掃描可能會導致更多的物理磁盤輸入輸出,但是全表掃描有時又可能會因爲高度並行化的存在而執行的更快。如果查詢的表完全沒有順序,那麼一個要返回記錄數小於
10%的查詢可能會讀取表中大部分的數據塊,這樣使用索引會使查詢效率提高很多。但是如果表非常有順序,那麼如果查詢的記錄數大於40
%時,可能使用全表掃描更快。因此,有一個索引範圍掃描的總體原則是:

a)         對於原始排序的表  僅讀取少於表記錄數40%的查詢應該使用索引範圍掃描。反之,讀取記錄數目多於表記錄數的40
%的查詢應該使用全表掃描。

*b)       * 對於未排序的表    僅讀取少於表記錄數7%的查詢應該使用索引範圍掃描。反之,讀取記錄數目多於表記錄數的7
%的查詢應該使用全表掃描。

 

隨着時間的推移和數據的累計與變化,ORACLE對SQL
語句的執行計劃也會改變,比如:基於代價的優化方法,隨着數據量的增大,優化器可能錯誤的不選擇索引而採用全表掃描。這種情況可能是因爲統計信息已經過時,在數據量變化很大後沒有及時分析表;但如果對錶進行分析之後,仍然沒有用上合理的索引,那麼就有必要對
SQL語句用HINT提示,強制用合理的索引。但這種HINT
提示也不能濫用,因爲這種方法過於複雜,缺乏通用性和應變能力,同時也增加了維護上的代價;相對來說,基於函數右移、去掉“IN ,OR ,<> ,IS
NOT NULL ”、分解複雜的SQL語句等等方法,卻是“放之四海皆準”的,可以放心大膽的使用。


 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章