MySQL 5.7 ref ——13.2.10.10優化子查詢
十、子查詢的優化
開發正在進行中,因此從長遠來看,沒有什么優化建議是可靠的。以下列表提供了一些您可能想要使用的有趣技巧。See also Section 8.2.2, “Optimizing Subqueries, Derived Tables, and View References”.
10.1 優化子查詢中行的數量或順序
SELECT * FROM t1 WHERE t1.column1 IN (SELECT column1 FROM t2 ORDER BY column1); SELECT * FROM t1 WHERE t1.column1 IN (SELECT DISTINCT column1 FROM t2); SELECT * FROM t1 WHERE EXISTS (SELECT * FROM t2 LIMIT 1);
10.2 用子查詢替換連接
SELECT DISTINCT column1 FROM t1 WHERE t1.column1 IN ( SELECT column1 FROM t2
);
用上面的語句替換這下面的:
SELECT DISTINCT t1.column1 FROM t1, t2 WHERE t1.column1 = t2.column1;
10.3
某些子查詢可以轉換為連接,以便與不支持子查詢的舊版MySQL兼容。但是,在某些情況下,將子查詢轉換為連接可能會提高性能。
10.4 子句從外部移動到子查詢內部
例如,用上面的查詢代替下面的:
SELECT * FROM t1 WHERE s1 IN (SELECT s1 FROM t1 UNION ALL SELECT s1 FROM t2); /*代替*/ SELECT * FROM t1 WHERE s1 IN (SELECT s1 FROM t1) OR s1 IN (SELECT s1 FROM t2);
另一個例子:
SELECT (SELECT column1 + 5 FROM t1) FROM t2; /*代替*/ SELECT (SELECT column1 FROM t1) + 5 FROM t2;
10.5 使用行子查詢而不是相關子查詢。
SELECT * FROM t1 WHERE (column1,column2) IN (SELECT column1,column2 FROM t2); /*代替*/ SELECT * FROM t1 WHERE EXISTS (SELECT * FROM t2 WHERE t2.column1=t1.column1 AND t2.column2=t1.column2);
10.6
使用
NOT (a = ANY (...))
而不是
a <> ALL (...)
10.7
使用
x = ANY (table containing (1,2))
而不是
x=1 OR x=2.
10.8
使用
=ANY
而不是
EXISTS
10.9
對於始終返回一行的不相關子查詢,IN 總是慢於 =
SELECT * FROM t1 WHERE t1.col_name = (SELECT a FROM t2 WHERE b = some_const); /*代替*/ SELECT * FROM t1 WHERE t1.col_name IN (SELECT a FROM t2 WHERE b = some_const);
這些技巧可能會導致程序變得更快或更慢。使用像BENCHMARK() 函數這樣的MySQL工具,您可以了解在您自己的情況下有什么幫助。See Section 12.15, “Information Functions”.
MySQL自己也會做出一些優化:
- MySQL只執行一次不相關的子查詢。使用EXPLAIN確保給定的子查詢確實不相關。
- MySQL會重寫IN,ALL,ANY和SOME子查詢,這樣是為了嘗試提高子查詢中的select-list列被索引的可能性。
- MySQL使用索引查找函數替換以下形式的子查詢,EXPLAIN將其描述為特殊的連接類型(unique_subquery或index_subquery:
... IN (SELECT indexed_column FROM single_table ...)
- MySQL使用包含MIN()或MAX()的表達式增強以下表單的表達式,除非涉及NULL值或空集:
value {ALL|ANY|SOME} {> | < | >= | <=} (uncorrelated subquery)
例如,對這個WHERE子句
WHERE 5 > ALL (SELECT x FROM t)
優化器可能會像這樣對待:
WHERE 5 > (SELECT MAX(x) FROM t)
See also MySQL Internals: How MySQL Transforms Subqueries.
十一、將子查詢重寫為連接
有時,除了使用子查詢之外,還有其他方法可以測試一組值中的成員資格。
同樣,在某些情況下,不僅可以將查詢重寫為沒有子查詢的語句,還可能比使用子查詢更加高效。IN()構造器就是如此。
例如,這個查詢:
SELECT * FROM t1 WHERE id IN (SELECT id FROM t2);
可以被重寫為:
SELECT DISTINCT t1.* FROM t1, t2 WHERE t1.id=t2.id;
查詢:
SELECT * FROM t1 WHERE id NOT IN (SELECT id FROM t2); SELECT * FROM t1 WHERE NOT EXISTS (SELECT id FROM t2 WHERE t1.id=t2.id);
可以被重寫為:
SELECT table1.* FROM table1 LEFT JOIN table2 ON table1.id=table2.id WHERE table2.id IS NULL;
LEFT [OUTER] JOIN可以比等效子查詢更快,因為服務器可能能夠更好地優化它——這個事實並非僅針對MySQL Server。
在SQL-92之前,外連接不存在,因此子查詢是執行某些操作的唯一方法。今天,MySQL Server和許多其他現代數據庫系統提供了廣泛的外連接類型。
MySQL Server支持多表DELETE語句,可用於根據一個表甚至多個表中的信息同時有效地刪除行。還支持多表UPDATE語句。See Section 13.2.2, “DELETE Syntax”, and Section 13.2.11, “UPDATE Syntax”.