文件首頁
MySQL 8.4 參考手冊
相關文件 下載本手冊
PDF (US Ltr) - 39.9Mb
PDF (A4) - 40.0Mb
Man Pages (TGZ) - 258.5Kb
Man Pages (Zip) - 365.5Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 8.4 參考手冊  /  ...  /  WHERE 子句最佳化

10.2.1.1 WHERE 子句最佳化

本節討論可以對處理 WHERE 子句進行的最佳化。範例使用 SELECT 語法,但相同的最佳化適用於 DELETEUPDATE 語法中的 WHERE 子句。

注意

由於 MySQL 最佳化工具的工作正在進行中,並非所有 MySQL 執行的最佳化都記錄在此處。

您可能會想重寫查詢以加快算術運算的速度,同時犧牲可讀性。由於 MySQL 會自動執行類似的最佳化,您通常可以避免這項工作,並將查詢保留為更容易理解和維護的形式。MySQL 執行的一些最佳化如下

  • 移除不必要的括號

       ((a AND b) AND c OR (((a AND b) AND (c AND d))))
    -> (a AND b AND c) OR (a AND b AND c AND d)
  • 常數摺疊

       (a<b AND b=c) AND a=5
    -> b>5 AND b=c AND a=5
  • 移除常數條件

       (b>=5 AND b=5) OR (b=6 AND 5=5) OR (b=7 AND 5=6)
    -> b=5 OR b=6

    這發生在準備階段而不是最佳化階段,這有助於簡化聯結。如需更多資訊和範例,請參閱章節 10.2.1.9,“外部聯結最佳化”

  • 索引使用的常數表達式只會評估一次。

  • 檢查數值類型的欄位與常數值的比較,並摺疊或移除無效或超出範圍的值

    # CREATE TABLE t (c TINYINT UNSIGNED NOT NULL);
      SELECT * FROM t WHERE c < 256;
    -≫ SELECT * FROM t WHERE 1;

    如需更多資訊,請參閱章節 10.2.1.14,“常數摺疊最佳化”

  • 在沒有 WHERE 子句的情況下,單一資料表上的 COUNT(*) 會直接從 MyISAMMEMORY 資料表的資料表資訊中擷取。這也適用於僅與單一資料表搭配使用的任何 NOT NULL 表達式。

  • 早期偵測無效的常數表達式。MySQL 會快速偵測到某些 SELECT 語法是不可能的,並且不傳回任何列。

  • 如果您不使用 GROUP BY 或彙總函式(COUNT()MIN() 等),HAVING 會與 WHERE 合併。

  • 對於聯結中的每個資料表,會建構一個更簡單的 WHERE,以快速評估資料表的 WHERE,並儘快跳過列。

  • 所有常數資料表會在查詢中的任何其他資料表之前讀取。常數資料表可以是下列任何一種

    • 空資料表或只有一列的資料表。

    • 一個在 PRIMARY KEYUNIQUE 索引上使用 WHERE 子句的資料表,其中所有索引部分都與常數表達式進行比較,並且定義為 NOT NULL

    以下所有資料表都被視為常數資料表

    SELECT * FROM t WHERE primary_key=1;
    SELECT * FROM t1,t2
      WHERE t1.primary_key=1 AND t2.primary_key=t1.id;
  • 要聯結資料表,最佳的聯結組合是透過嘗試所有可能性來找到。如果 ORDER BYGROUP BY 子句中的所有欄位都來自同一個資料表,則在聯結時會優先選擇該資料表。

  • 如果存在 ORDER BY 子句和不同的 GROUP BY 子句,或者 ORDER BYGROUP BY 包含來自聯結佇列中第一個資料表以外的資料表之欄位,則會建立一個暫存資料表。

  • 如果您使用 SQL_SMALL_RESULT 修飾詞,MySQL 會使用記憶體內的暫存資料表。

  • 會查詢每個資料表索引,並使用最佳索引,除非優化器認為使用資料表掃描效率更高。過去,掃描是根據最佳索引是否跨越超過資料表的 30% 來決定是否使用,但固定百分比不再決定使用索引或掃描之間的選擇。現在的優化器更複雜,其估計基於額外的因素,例如資料表大小、行數和 I/O 區塊大小。

  • 在某些情況下,MySQL 可以從索引讀取資料列,甚至無需查詢資料檔案。如果索引中使用的所有欄位都是數值型,則只會使用索引樹來解析查詢。

  • 在輸出每行之前,會跳過那些不符合 HAVING 子句的行。

一些速度非常快的查詢範例

SELECT COUNT(*) FROM tbl_name;

SELECT MIN(key_part1),MAX(key_part1) FROM tbl_name;

SELECT MAX(key_part2) FROM tbl_name
  WHERE key_part1=constant;

SELECT ... FROM tbl_name
  ORDER BY key_part1,key_part2,... LIMIT 10;

SELECT ... FROM tbl_name
  ORDER BY key_part1 DESC, key_part2 DESC, ... LIMIT 10;

假設索引欄位為數值型,MySQL 會僅使用索引樹解析以下查詢

SELECT key_part1,key_part2 FROM tbl_name WHERE key_part1=val;

SELECT COUNT(*) FROM tbl_name
  WHERE key_part1=val1 AND key_part2=val2;

SELECT MAX(key_part2) FROM tbl_name GROUP BY key_part1;

以下查詢使用索引以排序順序檢索資料列,而無需單獨的排序步驟

SELECT ... FROM tbl_name
  ORDER BY key_part1,key_part2,... ;

SELECT ... FROM tbl_name
  ORDER BY key_part1 DESC, key_part2 DESC, ... ;