sql查詢中索引失效的原因

2021-10-09 22:15:25 字數 4027 閱讀 8399

1. where語句中包含or時,可能會導致索引失效

使用or並不是一定會使索引失效,你需要看or左右兩邊的查詢列是否命中相同的索引。

假設user表中的user_id列有索引,age列沒有索引。

下面這條語句其實是命中索引的(據說是新版本的mysql才可以,如果你使用的是老版本的mysql,可以使用explain驗證下)。

select

*from

`user

`where user_id =

1or user_id =

2;

但是這條語句是無法命中索引的。

select

*from

`user

`where user_id =

1or age =

20;

假設age列也有索引的話,依然是無法命中索引的。

select

*from

`user

`where user_id =

1or age =

20;

因此才有建議說,盡量避免使用or語句,可以根據情況盡量使用union all或者in來代替,這兩個語句的執行效率也比or好些。

2. where語句中索引列使用了負向查詢,可能會導致索引失效

負向查詢包括:not、!=、<>、!<、!>、not in、not like等。

某「軍規」中說,使用負向查詢一定會索引失效,筆者查了些文章,有網友對這點進行了反駁並舉證。

其實負向查詢並不絕對會索引失效,這要看mysql優化器的判斷,全表掃瞄或者走索引哪個成本低了。

3. 索引字段可以為null,使用is null或is not null時,可能會導致索引失效

其實單個索引字段,使用is null或is not null時,是可以命中索引的,但網友在舉證時說兩個不同索引欄位用or連線時,索引就失效了,筆者認為確實索引失效,但這個鍋應該由or來背,屬於第一種場景~~

假設user表中的user_id列有索引且允許null,age列有索引且允許null。

select

*from

`user

`where user_id is

notnull

or age is

notnull

;

不過某些「軍規」和規範中都有強調,欄位要設為not null並提供預設值,是有原因值得參考的。

4. 在索引列上使用內建函式,一定會導致索引失效

比如下面語句中索引列login_time上使用了函式,會索引失效:

select

*from

`user

`where date_add(login_time,

interval

1day)=

7;

優化建議,盡量在應用程式中進行計算和轉換。

其實還有網友提到的兩種索引失效場景,應該都歸於索引列使用了函式。

4.1 隱式型別轉換導致的索引失效

比如下面語句中索引列user_id為varchar型別,不會命中索引:

select

*from

`user

`where user_id =

12;

這是因為mysql做了隱式型別轉換,呼叫函式將user_id做了轉換。

select

*from

`user

`where cast(user_id as signed int)=

12;

4.2 隱式字元編碼轉換導致的索引失效

當兩個表之間做關聯查詢時,如果兩個表中關聯的字段字元編碼不一致的話,mysql可能會呼叫convert函式,將不同的字元編碼進行隱式轉換從而達到統一。作用到關聯的字段時,就會導致索引失效。

比如下面這個語句,其中d.tradeid字元編碼為utf8,而l.tradeid的字元編碼為utf8mb4。因為utf8mb4是utf8的超集,所以mysql在做轉換時會用convert將utf8轉為utf8mb4。簡單來看就是convert作用到了d.tradeid上,因此索引失效。

select l.operator from tradelog l , trade_detail d where d.tradeid=l.tradeid and d.id=

4;

這種情況一般有兩種解決方案。

方案1: 將關聯欄位的字元編碼統一。

方案2: 實在無法統一字元編碼時,手動將convert函式作用到關聯時=的右側,起到字元編碼統一的目的,這裡是強制將utf8mb4轉為utf8,當然從超集向子集轉換是有資料截斷風險的。如下:

select d.

*from tradelog l , trade_detail d where d.tradeid=

convert

(l.tradeid using utf8)

and l.id=

2;

5. 對索引列進行運算,一定會導致索引失效

運算如+,-,*,/等,如下:

select

*from

`user

`where age -1=

10;

優化的話,要把運算放在值上,或者在應用程式中直接算好,比如:

select

*from

`user

`where age =10-

1;

6. like萬用字元可能會導致索引失效

like查詢以%開頭時,會導致索引失效。解決辦法有兩種:

將%移到後面,如:

select

*from

`user

`where

`name`

like

'李%'

;

利用覆蓋索引來命中索引。

select name from

`user

`where

`name`

like

'%李%'

;

7. 聯合索引中,where中索引列違背最左匹配原則,一定會導致索引失效

當建立乙個聯合索引的時候,如(k1,k2,k3),相當於建立了(k1)、(k1,k2)和(k1,k2,k3)三個索引,這就是最左匹配原則。

比如下面的語句就不會命中索引:

select

*from t where k2=2;

select

*from t where k3=3;

slect *

from t where k2=

2and k3=

3;

下面的語句只會命中索引(k1):

slect *

from t where k1=

1and k3=

3;

8. mysql優化器的最終選擇,不走索引

上面有提到,即使完全符合索引生效的場景,考慮到實際資料量等原因,最終是否使用索引還要看mysql優化器的判斷。當然你也可以在sql語句中寫明強制走某個索引。

優化索引的一些建議

禁止在更新十分頻繁、區分度不高的屬性上建立索引。 更新會變更b+樹,更新頻繁的字段建立索引會大大降低資料庫效能。

「性別」這種區分度不大的屬性,建立索引是沒有什麼意義的,不能有效過濾資料,效能與全表掃瞄類似。

建立組合索引,必須把區分度高的字段放在前面。

索引失效的原因

1 隱式轉換導致索引失效.這一點應當引起重視.也是開發中經常會犯的錯誤.由於表的字段tu mdn定義為varchar2 20 但在查詢時把該字段作為number型別以where條件傳給oracle,這樣會導致索引失效.錯誤的例子 select from test where tu mdn 13333...

like索引失效原因 索引失效的情況及原因定位

同事遇到乙個奇葩的問題,乙個表裡某個欄位建了索引,但是有的值走索引,有的值不走索引。因為一般情況乙個字段要麼完全不走索引,要麼走索引,怎麼會有的值走索引,有的不走索引。select 條件非常簡單,因為涉及到敏感資訊就不貼表結構了。例如select from order where status 2 ...

索引失效原因分類

1 沒有查詢條件,或者查詢條件沒有建立索引 2 在查詢條件上沒有使用復合索引引導列 3 查詢的資料量超過了表資料量的15 4 索引本身失效 status unusable 5 查詢條件使用函式在索引列上 6 對小表查詢 7 使用hint提示不使用索引 8 統計資訊不真實 9 cbo認為走全表掃瞄比走...