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


Oracle建立索引的目的是為了避免全表掃描,提高查詢的效率。

但是有些情況下,即使建立了索引,但是執行寫出來的查詢還是很慢,然后通過執行計划會發現是索引失效導致的(不走索引,走全表掃描)。所以需要了解一下有哪些些情況會導致索引失效,即查詢不走索引的原因。

在寫SQL的層面上一些騷操作會導致索引失效

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

既然沒有WHERE子句,那么就是查詢全部數據了,相當於全表掃描,當然不走索引了。

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

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

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

-- 創建包含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.索引本身失效。這種情況就很玄乎了,只能重建索引試試。 

 

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM