1. 程式人生 > 實用技巧 >SQL語句優化-範圍優化

SQL語句優化-範圍優化

範圍優化

範圍指的是通過單個索引檢索一個或多個索引值間隔內包含的行記錄。可用於單列索引和複合索引

單列索引的範圍訪問

單列索引的範圍條件定義如下:

  • 對於BTREE和HASH索引,在使用=、<=>、IN()、IS NULL或IS NOT NULL運算子時,將欄位與常量值進行比較是一個範圍條件
  • 另外,對於BTREE索引,使用>、<、>=、<=、BETWEEN、!=、或<>運算子,或LIKE比較(如果LIKE的引數是不以萬用字元開頭的常量字串),這些也是範圍條件
  • 對於所有索引型別,多個範圍條件與OR或AND組合形成範圍條件

上面提到的常量值是指如下:

  • 查詢字串中的常量
  • 和常量表(const)或系統表(system)聯接的列
  • 不相關的子查詢的結果
  • 完全由前面三條組成的表示式

單列索引範圍查詢示例

 SELECT * FROM t1 WHERE key_col > 1 AND key_col < 10;
 
 SELECT * FROM t1 WHERE key_col = 1 OR key_col IN (15,18,20);
 
 SELECT * FROM t1 WHERE key_col LIKE 'ab%' OR key_col BETWEEN 'bar' AND 'foo';

在優化器常數傳播階段,一些非常數值可以轉換為常數。

MySQL嘗試從WHERE子句中提取每個索引的範圍條件。在提取過程中,去掉了不能用於構造範圍條件的條件,合併了產生重疊範圍的條件,去掉了產生空範圍的條件。

舉一個例子:

下面語句中key1是有索引的列,而nonkey列不具備索引

 SELECT * FROM t1 WHERE
   (key1 < 'abc' AND (key1 LIKE 'abcde%' OR key1 LIKE '%b')) OR
   (key1 < 'bar' AND nonkey = 4) OR
   (key1 < 'uux' AND key1 > 'z');

那麼MySQL會嘗試根據key1進行範圍條件提取,提取演算法如下:

  1. 先提取出WHERE關鍵字後的語句:
     (key1 < 'abc' AND (key1 LIKE 'abcde%' OR key1 LIKE '%b')) OR
     (key1 < 'bar' AND nonkey = 4) OR
     (key1 < 'uux' AND key1 > 'z')
    
  2. 刪除nonkey = 4和key1 LIKE '% b',因為它們不能用於範圍掃描。刪除它們的正確方法是將它們替換為TRUE,這樣在進行範圍掃描時就不會遺漏任何匹配的行。用TRUE批量代替它們:
     (key1 < 'abc' AND (key1 LIKE 'abcde%' OR TRUE)) OR
     (key1 < 'bar' AND TRUE) OR
     (key1 < 'uux' AND key1 > 'z')
    
  3. 使用常量替代一些始終為true或者false的條件:
     (key1 < 'abc' AND TRUE) OR (key1 < 'bar' AND TRUE) OR (FALSE)
    
    然後刪除一些不必要的true或者false:
     (key1 < 'abc') OR (key1 < 'bar')
    
  4. 將重疊區間合併為一個區間,就得到了用於距離掃描的最終條件:
     (key1 < 'bar')
    

一般來說(如前面的例子所示),用於範圍掃描的條件比WHERE子句限制性小。MySQL執行額外的檢查,以篩選出滿足範圍條件但不滿足完整WHERE子句的行記錄。

範圍條件提取演算法可以處理任意深度的巢狀AND/OR結構,其輸出不依賴於WHERE子句中條件出現的順序。

MySQL不支援為空間索引的範圍訪問合併。為了繞過這個限制,您可以使用具有相同SELECT語句的UNION,只是將每個空間謂詞放在不同的SELECT中。

複合索引的範圍訪問

複合索引上的範圍條件是單列索引範圍條件的擴充套件。由複合索引上的範圍條件定義的區間限制複合索引位於一個或多個鍵元組之間。在一組鍵元組上定義的鍵元組區間按照索引順序排序。

例如,考慮一個定義為key1(key_part1, key_part2, key_part3)的複合索引,並且按鍵順序列出的以下一組鍵元組:

 key_part1  key_part2  key_part3
   NULL       1          'abc'
   NULL       1          'xyz'
   NULL       2          'foo'
    1         1          'abc'
    1         1          'xyz'
    1         2          'abc'
    2         1          'aaa'

條件key_part1=1定義此區間:

 (1,-inf,-inf) <= (key_part1,key_part2,key_part3) < (1,+inf,+inf)

該區間包含第4、5、6組鍵元組。

相比之下,條件key_part3='abc'沒有定義區間,是無序的。

下面的描述更詳細地說明了範圍條件如何為複合索引工作。

  1. 對於HASH索引,只能在以下情況下生成區間:
         key_part1 cmp const1
     AND key_part2 cmp const2
     AND ...
     AND key_partN cmp constN;
    
    這裡,const1, const2,…是常量,cmp是=,<=>,或者IS NULL這些比較運算子之一,條件必須覆蓋所有索引部分(也就是說,有N個條件,複合索引的每一部分對應1個條件)。舉一個例子:
     key_part1 = 1 AND key_part2 IS NULL AND key_part3 = 'foo'
    
  2. 對於BTREE索引,一個區間通過使用AND條件組合而成,其中每個條件使用=,<=>,IS NULL,>,<,>=,<=,!=,<>,BETWEEN或LIKE "pattern"(其中"pattern"不以萬用字元開頭)。只要有一個包含符合條件的所有行的單鍵元組,就可以使用區間(如果使用<>或!=,則為兩個間隔)。
    只要比較運算子為=、<=>或IS NULL,優化器就會嘗試使用複合索引的其他部分來確定區間。如果運算子是>,<,>=,<=!=、<>、BETWEEN或諸如此類,優化器使用它,但不再考慮其他部分。對於下面的表示式,優化器從第一個比較中使用=,這樣會使用來自第二個比較的>=,但不考慮其他部分,也不將第三個比較用於區間:
     key_part1 = 'foo' AND key_part2 >= 10 AND key_part3 > 10
    
    根據範圍條件定義的區間是這樣的:
     ('foo',10,-inf) < (key_part1,key_part2,key_part3) < ('foo',+inf,+inf)
    
  3. 如果是並行關係(即使用OR連線),則生成一個並行區間:
     (key_part1 = 1 AND key_part2 < 2) OR (key_part1 > 5)
    
    根據範圍條件定義的區間是這樣的:
     (1,-inf) < (key_part1,key_part2) < (1,2)
     (5,-inf) < (key_part1,key_part2)
    
    可以使用EXPLAIN輸出鍵字首的最大長度,key_len這一列表示所使用的鍵長度
    在某些情況下,key_len可能表示使用了複合索引的某一部分,但這可能不是您所期望的。假設key_part1和key_part2可以為空。然後key_len列顯示以下條件下的兩個關鍵部分長度:
     key_part1 >= 1 AND key_part2 < 2
    
    但實際上,條件被轉換為:
     key_part1 >= 1 AND key_part2 IS NOT NULL
    
    實際使用鍵的長度是key_part1的長度