Oracle Index 索引無效原因

2021-10-11 18:53:30 字數 3606 閱讀 5010

最近遇到乙個sql語句的效能問題,修改功能之前的執行時間平均為0.3s,可是新增新功能後,時間達到了4~5s。雖然幾張表的資料量都比較大(都在百萬級以上),但是也都有正確建立索引,不知道到底慢在了**,下面展開調查。

經過幾次排除,把問題範圍縮小在索引上,首先在確定索引本身沒有問題的前提下,考慮索引有沒有被使用到,那麼新的問題來了,怎麼知道指定索引是否被啟用。

1. 分析索引

即將索引至於監控狀態下,對索引進行分析。如下對id_tt_shohou_hist_002索引進行分析

alter index id_tt_shohou_hist_002 monitoring usage;
2. 檢視v$object_usage檢視中記錄的資訊

字段依次為:

如上圖,雖然索引已經被引用,但是速度依舊很慢,莫非是雖然啟用了索引,但是又被其他的一些原因拖慢了速度,繼續調查。

調查途中,收集到一些oracle 資料庫不走索引的原因分享給大家

1. 在索引列上使用函式時不會使用索引

例如常見的,to_charto_dateto_numbertrunc...等等。

此時的解決辦法可以使用函式索引,顧名思義就是把使用函式後的字段整體當成索引中的字段。

如下圖中的to_char(shohou_date, 'yyyymmdd')就是乙個函式索引,因為日期欄位中含有時分秒,進行日期比較的時候,必須轉化成固定的格式。

create index id_tt_shohou_hist_003

on tt_shohou_hist

(del_***,to_char(shohou_date, 'yyyymmdd'), shohou_id)

tablespace salespa_index

2. 索引的列進行隱式的型別轉換

select * from table where index_colum = 5
上面語句中的index_colum字段型別為varchar2,這時就會發生隱式型別轉換,類似於

select * from table where to_number(index_colum) = 5
3. where 子句中使用不等於操作

不等於操作包括:<>,!=,not colum >= ?,not colum <= ?

替代方式可以使用or, colum <> 0 *****> colum > 0 or colum < 0;

4. 使用 is null 和 is not null

替代方式:函式索引

通過nvl(b,c)將為空的字段轉為不為空的c值,再在函式nvl(b,c)上建立函式索引

轉換前

select * from a where b = null
轉換後

select * from a where nvl(b,c) = c
5. 組合索引

組合索引:由多個列構成的索引。如

create index index_emp on emp (col1,col2,col3,...)
index_emp則為復合索引,col1為引導列。進行查詢時,可以使用where col1 = ?,也可以使用where col1 = ? and col2 = ?,這樣的限制條件都會使用索引,但是where col2 = ?,不會使用索引,所以限制條件中包含引導列時,該限制條件才會使用組合索引。

經過一番調查,我使用的sql語句檢索條件中對時間列進行to_char(ttsh.shohou_date, 'yyyymmdd')格式化日期,去除掉時分秒。再建立函式索引後仍然沒有起到優化加速的效果,仔細觀察發現在使用to_char格式化時間之後,又進行to_date轉為時間格式和其他子查詢的字段進行比較。然後很快想到,建立乙個to_date(to_char(ttsh.shohou_date, 'yyyymmdd'), 'yyyymmdd')這樣的函式索引,結果缺失提高了不少的執行速度,從4~5s縮短到了0.5s左右。

但是這只是在pl/sql軟體中執行sql提高了速度,實際專案執行仍然是4~5s,使用語句檢視索引的使用狀況時,發現並沒有使用索引,但是在pl/sql軟體中確實呼叫了索引,這至今都是未解之謎,如果有大神知道原因希望能幫我解答一下這個疑問。

既然不能自動呼叫,只能強制讓sql走指定索引了,強制的方法如下

select語句後加入/*+index(ttsh id_tt_shohou_hist_002)*/,其中ttsh是表的別名(當表有別名的時候,必須在索引前加入表的別名)

select /*+index(ttsh id_tt_shohou_hist_002)*/ 

to_date(to_char(ttsh.shohou_date, 'yyyymmdd'), 'yyyymmdd') as shohou_date

from tt_shohou_hist ttsh

where ...

至此,sql的效率問題已經解決了,但是這不是最好的解決方案。

首先,目前的索引中已經存在包含to_char(ttsh.shohou_date, 'yyyymmdd')的函式索引,又再建立乙個to_date(to_char(ttsh.shohou_date, 'yyyymmdd'), 'yyyymmdd'),看著就很難受

其次,強制使用索引的方法需要在sql中指定索引名,假如資料庫中的索引名發生變更,還需去更改sql。

最好的方法是把索引欄位的to_date去掉,統一使用to_char的索引。

and cal.calender = to_date(to_char(ttsh.shohou_date, 'yyyymmdd'), 'yyyymmdd')
上面的部分語句因為calender欄位是date型別,所以比較時使用了to_date,其實只要把calender轉化成char型別就行了,雖然看起來要改動的地方很多,其實解決了更大的問題。

(摘抄)oracle index 索引

小白開始學習索引之路 大表,返回的行數 5 經常使用where子句查詢的列 離散度高的列 更新鍵值代價低 邏輯and,or 效率高 檢視索引在建在那錶那列 唯一索引 唯一索引指鍵值不重複。create unique index empno idx on emp empno drop index em...

Oracle index 索引提示解析

使用 hints 時,在某些情況下,為了確保讓優化器產生最優的執行計畫,我們可能指定全套的 hints oracle 索引提示,oracle index 提示,oracle index tips oracle index 優化,oracle index 提示。指示優化器的方法與目標的 hints a...

索引無效的幾種情況

檢查被索引的列或組合索引的首列是否出現在pl sql語句的where子句中,這是 執行計畫 能用到相關索引的必要條件。看採用了哪種型別的連線方式。oracle的共有sort merge join smj hash join hj 和nested loop join nl 在兩張表連線,且內錶的目標列...