oracle查詢不走索引的一些狀況(索引失效)

Oracle創建索引的目的是爲了不全表掃描,提升查詢的效率。程序員

可是有些狀況下,即便創建了索引,可是執行寫出來的查詢仍是很慢,而後經過執行計劃會發現是索引失效致使的(不走索引,走全表掃描)。因此須要瞭解一下有哪些些狀況會致使索引失效,即查詢不走索引的緣由。函數

在寫SQL的層面上一些騷操做會致使索引失效優化

沒有寫WHERE子句或查詢條件沒有創建索引spa

既然沒有WHERE子句,那麼就是查詢所有數據了,至關於全表掃描,固然不走索引了。code

而查詢條件上沒有創建索引的話,索引都沒有還走個毛索引啊。blog

WHERE子句上沒有使用索引中的引導列索引

要使用索引,則查詢條件中必須包含索引中的引導列。好比一個複合索引包含A,B,C,D四列,則A爲引導列(排在第一位置的列)。若是WHERE子句中所包含的列是BCD或者BD等狀況,則只能使用非匹配索引掃描。class

-- 建立包含A,B,C,D四列的複合索引
CREATE INDEX INDEX_ABCD ON LETTERS(A, B, C, D);
-- 下列語句不會使用複合索引
SELECT * FROM LETTERS WHERE B = 'b' AND C = 'c'; SELECT * FROM LETTERS WHERE B = 'b' AND D = 'd'; SELECT * FROM LETTERS WHERE C = 'c' AND D = 'd'; SELECT * FROM LETTERS WHERE B = 'b' AND C = 'c' AND D = 'd';

另外,單獨引用複合索引裏排第一位置的索引列也會致使索引失效,複合索引必須複合使用才能生效。效率

-- 單獨使用複合索引中的引導列也不會觸發複合索引
SELECT * FROM LETTERS WHERE A = 'a'

WHERE子句中使用IS NULL或IS NOT NULL數據類型

使用判斷空或非空的條件會致使該索引列失效。

-- COMM列的索引會失效
SELECT * FROM EMP WHERE COMM IS NULL;

WHERE子句中使用函數

若是沒有使用基於函數的索引,那麼WHERE子句中對存在索引的列使用函數時,會使優化器忽略這些索引。

-- 在沒有創建基於函數索引的字段上使用了函數,會致使索引失效
SELECT * FROM STUFF WHERE TRUNC(BIRTHDAY) = '2019-04-01'

非要這樣的話能夠經過在索引列上創建基於函數的索引來解決問題。

-- 給BIRTHDAY列建立TRUNC函數的函數索引
CREATE INDEX STUFF_BIRTHDAY_FBI_IDX ON STUFF(TRUNC(BIRTHDAY));

這樣的話函數索引就起做用了,也就能在索引列上使用函數了,並且這時候你想不用函數都不行。

可是對於MIN,MAX函數等,Oracle仍然會使用索引,由於Oracle對這類聚合分析函數作了相應的優化。

對索引列進行運算致使索引失效

和使用函數一樣的,若是使用運算,好比加減乘除非等,也會致使索引失效。

-- 對索引列進行了減法運算,致使不走索引
SELECT * FROM USERS WHERE AGE - 1 = 23

-- 正確的寫法(去除運算)
SELECT * FROM USERS WHERE AGE = 24

使用前導模糊查詢

前導模糊查詢的寫法是LIKE '%T',即通配符【%】寫在要匹配的字符前面,這樣的狀況下不走索引。

-- 前導模糊查詢不走索引
SELECT * FROM LOVERS WHERE NAME LIKE '%靜';

WHERE子句中使用不等於條件

不等於操做包括:

<>
!=
NOT COL >= ? NOT COL <= ?

爲何不等於條件會不走索引,咱們能夠用常規的思惟去推斷一下。首先【不等於】這個概念能夠推斷成選取結果集中的一個比較大的部分,好比咱們要找出中國人口中不是姓楊的,要找出資深程序員中不會禿頭的,均可以認爲是要返回結果集中一個很大的部分。由於Oracle的CBO是閉源的,咱們只能推斷Oracle會認爲既然要返回結果集中很大的一個部分,不如直接使用全表掃描而不考慮索引。

並且Oracle是建議,對於這些限制條件可使用OR代替,例如COL <> 0能夠替換成COL > 0 OR COL < 0。

WHERE子句中使用NOT IN或NOT EXISTS

使用NOT IN或NOT EXISTS也會致使不走索引。

事實上NOT IN和NOT EXISTS也能夠看作是不等於條件,不走索引的緣由和上面的不等於條件相同。

另外有一些人說使用IN是不走索引的,這是不對的,IN是能夠走索引的,只是可能效率會比EXISTS低。

等於和範圍索引不會被合併使用

SELECT * FROM CARS WHERE COLOR = 'yellow' AND TYPE = 'B'

在上面的查詢中,COLOR和TYPE列上都建立了非惟一索引,在這種查詢條件下Oracle並不會合併索引,而只會使用第一個索引,即只有COLOR列上索引會生效。

比較不匹配類型的數據類型

好比SERIAL_CODE是一個VARCHAR2類型的字段,在這個字段上創建了索引,可是下面的語句將會執行全表掃描而不走索引。

SELECT * FROM MATERIALS WHERE SERIAL_CODE = 810646874;

爲何呢?由於Oracle中有一個字段自動進行隱式類型轉換的機制,會自動把類型不匹配的字段進行隱式類型轉換,至關於:

SELECT * FROM MATERIALS WHERE TO_NUMBER(SERIAL_CODE) = 810646874;

能夠看出,Oracle優化器自動給SERIAL_CODE加上了類型轉換函數,這樣就限制了索引的使用。

因此正確的寫法應該是條件和字段類型相匹配:

SELECT * FROM MATERIALS WHERE SERIAL_CODE = '810646874';

表中數據量的多少也會影響索引的使用

查詢的數量是大表的大部分,應該是30%以上

若是查詢的數量超過大表數量的30%,那就不走索引了。

對小表查詢

舉個極端的例子,表中只有一條數據,何須走索引呢。好比你看一本只有幾頁的書,難道你還會去看目錄嗎,給這本書建目錄都是人才了,你還去找這本書有沒有目錄豈不是人才中的人才(你別去上班了,我建目錄養你啊)。

索引失效的解決辦法

下面這些解決辦法是基於SQL寫得沒問題,而索引就是不生效的狀況。

選用合適的Oracle優化器

Oracle的優化器有三種,一種是基於規則的(RULE),一種是基於成本的(COST),還有一種是選擇性的(CHOOSE)。

在缺省的狀況下(未設置),Oracle默認採用CHOOSE優化器。爲了不那些沒必要要的全表掃描(FULL TABLE SCAN),你必須儘可能避免使用CHOOSE優化器,而直接採用基於規則或基於成本的優化器。

重建索引

和電腦出了問題的重啓試試同樣,索引出了問題也是能夠重建試試的。

ALTER INDEX 索引名 REBUILD

強制索引

強制索引是使用hint關鍵字。

正常使用的索引忽然失效的對應解決辦法

有些時候,一樣的SQL,以前是能走索引的,忽然有一天不走索引了,那麼多是:

1.隨着表的增加,WHERE條件出來的數據太多,大於15%,致使CBO計算出走索引掃描大於走全表掃描,就會使得索引失效。這種狀況目前我也不知道該怎麼辦(好難啊)。

2.統計信息失效。這種狀況須要從新蒐集統計信息。

3.索引自己失效。這種狀況就很玄乎了,只能重建索引試試。 

 

"人始終要走到某個路口,看着一路的同伴有些往左走,有些往右走,只剩下本身一個左顧右盼,不知所措。要麼跟着大部隊往左往右,要麼就朝前走,絕沒有任何退後的可能。"

相關文章
相關標籤/搜索