1. 程式人生 > >哪些sql語句不會執行索引

哪些sql語句不會執行索引

1、查詢謂詞沒有使用索引的主要邊界,換句話說就是select *,可能會導致不走索引。

比如,你查詢的是SELECT * FROM T WHERE Y=XXX;假如你的T表上有一個包含Y值的組合索引,但是優化器會認為需要一行行的掃描會更有效,這個時候,優化器可能會選擇TABLE ACCESS FULL,但是如果換成了SELECT Y FROM T WHERE Y = XXX,優化器會直接去索引中找到Y的值,因為從B樹中就可以找到相應的值。

2、單鍵值的b樹索引列上存在null值,導致COUNT(*)不能走索引。

如果在B樹索引中有一個空值,那麼查詢諸如SELECT COUNT(*) FROM T 的時候,因為HASHSET中不能儲存空值的,所以優化器不會走索引,有兩種方式可以讓索引有效,一種是SELECT COUNT(*) FROM T WHERE XXX IS NOT NULL或者把這個列的屬性改為not null (不能為空)。

3、索引列上有函式運算,導致不走索引

如果在T表上有一個索引Y,但是你的查詢語句是這樣子SELECT * FROM T WHERE FUN(Y) = XXX。這個時候索引也不會被用到,因為你要查詢的列中所有的行都需要被計算一遍,因此,如果要讓這種sql語句的效率提高的話,在這個表上建立一個基於函式的索引,比如CREATE INDEX IDX FUNT ON T(FUN(Y));這種方式,等於Oracle會建立一個儲存所有函式計算結果的值,再進行查詢的時候就不需要進行計算了,因為很多函式存在不同返回值,因此必須標明這個函式是有固定返回值的。

4、隱式轉換導致不走索引。

索引不適用於隱式轉換的情況,比如你的SELECT * FROM T WHERE Y = 5 在Y上面有一個索引,但是Y列是VARCHAR2的,那麼Oracle會將上面的5進行一個隱式的轉換,SELECT * FROM T WHERE TO_NUMBER(Y) = 5,這個時候也是有可能用不到索引的。

5、表的資料庫小或者需要選擇大部分資料,不走索引

在Oracle的初始化引數中,有一個引數是一次讀取的資料塊的數目,比如你的表只有幾個資料塊大小,而且可以被Oracle一次性抓取,那麼就沒有使用索引的必要了,因為抓取索引還需要去根據rowid從資料塊中獲取相應的元素值,因此在表特別小的情況下,索引沒有用到是情理當中的事情。

6、cbo優化器下統計資訊不準確,導致不走索引

很長時間沒有做表分析,或者重新收集表狀態資訊了,在資料字典中,表的統計資訊是不準確的,這個情況下,可能會使用錯誤的索引,這個效率可能也是比較低的。

7、!=或者<>(不等於),可能導致不走索引,也可能走 INDEX FAST FULL SCAN

例如select id  from test where id<>100

8、表字段的屬性導致不走索引,字元型的索引列會導致優化器認為需要掃描索引大部分資料且聚簇因子很大,最終導致棄用索引掃描而改用全表掃描方式,

由於字元型和數值型的在insert的時候排序不同,字元型別導致了聚簇因子很大,原因是插入順序與排序順序不同。詳細點說,就是按照數字型別插入(1..3200000),按字元型別('1'...'32000000')t排序,在對字元型別使用大於運算子時,會導致優化器認為需要掃描索引大部分資料且聚簇因子很大,最終導致棄用索引掃描而改用全表掃描方式。

下面展示測試結果,

兩個表的資料型別相似(只是ID欄位型別不同),各插入了320萬資料,ID欄位範圍為1~3200000。

模擬場景

相關程式碼如下:

對於普通的採用數值型別的欄位,範圍查詢就是正常的索引範圍掃描,執行效率很高。

對於文字型別欄位的表,範圍查詢就是對應的全表掃描,效率較低是顯而易見的。

解決方法

將SQL語句由開放區間掃描(>=),修改為封閉區間(between xxx and max_value)。使得資料在索引區域性順序是“對的”。如果採用這種方式仍然不走索引掃描,還可以進一步細化分段或者採用“逐條提取+批繫結”的方法。

9.建立組合索引,但查詢謂詞並未使用組合索引的第一列,此處有一個INDEX SKIP SCAN概念,

10、like '%liu' 百分號在前

11,not in ,not exist

可以嘗試把not in 或者 not exsts改成左連線的方式(前提是有子查詢,並且子查詢有where條件)。

例如:

SELECT

    /*+ INDEX(I CIRCLEICONMAST_IX1)*/

    I.ICONNO,

    I.CIRCLEID,

    I.FILEPATH,

    I.REGDT,

    I.FILEPATH || '/' || I.FILENAME IMGNAME,

    I.FILEPATH || '/' || 'th_160_' || I.FILENAME SMALLIMGNAME,

    I.MEMBERID,

    I.ADMCHK STATUS,

    I.ADMCHK ORIGINALSTATUS,

    ROWNUM RN

     FROM CIRCLEICONMAST I

    WHERE I.REGDT BETWEEN TO_DATE('20120619', 'YYYYMMDD') - 10000 AND

      TO_DATE('20120621', 'YYYYMMDD')

      AND NOT EXISTS (

       SELECT C.VALIDFLG

         FROM CIRCLEMAST C

        WHERE C.VALIDFLG IN ('N', 'F')

        AND I.CIRCLEID = C.CIRCLEID)      

      AND I.ADMCHK = 'N'

改成左連線:

SELECT

/*+ INDEX(I CIRCLEICONMAST_IX1)*/

I.ICONNO,

I.CIRCLEID,

I.FILEPATH,

I.REGDT,

I.FILEPATH || '/' || I.FILENAME IMGNAME,

I.FILEPATH || '/' || 'th_160_' || I.FILENAME SMALLIMGNAME,

I.MEMBERID,

I.ADMCHK STATUS,

I.ADMCHK ORIGINALSTATUS,

ROWNUM RN

  FROM CIRCLEICONMAST I, CIRCLEMAST C

WHERE I.REGDT BETWEEN TO_DATE('20110620', 'YYYYMMDD') AND

       TO_DATE('20120621', 'YYYYMMDD') + 1

   AND C.VALIDFLG NOT IN ('N', 'F')

   AND I.CIRCLEID = C.CIRCLEID

   AND I.ADMCHK = 'N'

總結:oracle中有很多情況會導致index失效,並且走全表掃描的代價是相當大的,所以在寫sql的時候一定要注意這個會使索引失效的情況,養成良好的習慣。