本節討論可以對處理 WHERE
子句進行的最佳化。範例使用 SELECT
陳述式,但相同的最佳化也適用於 DELETE
和 UPDATE
陳述式中的 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(*)
時,會直接從MyISAM
和MEMORY
資料表的資料表資訊中擷取。當只使用一個資料表時,任何NOT NULL
運算式也會執行此動作。早期偵測無效的常數運算式。MySQL 會快速偵測到某些
SELECT
陳述式不可能,且不傳回任何資料列。如果您未使用
GROUP BY
或彙總函式(COUNT()
、MIN()
等),則HAVING
會與WHERE
合併。對於聯結中的每個資料表,會建構一個較簡單的
WHERE
,以取得該資料表的快速WHERE
評估,並儘快略過資料列。所有常數資料表都會在查詢中的任何其他資料表之前先讀取。常數資料表是下列任何一種
空的資料表或只有一個資料列的資料表。
在
PRIMARY KEY
或UNIQUE
索引上使用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 BY
和GROUP BY
子句中的所有資料行都來自同一個資料表,則聯結時會優先選取該資料表。如果存在
ORDER BY
子句和不同的GROUP BY
子句,或者如果ORDER BY
或GROUP 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, ... ;