如果您只需要結果集中指定數量的列,請在查詢中使用 LIMIT
子句,而不是擷取整個結果集並丟棄額外的資料。
MySQL 有時會最佳化具有 LIMIT
子句且沒有 row_count
HAVING
子句的查詢
如果您使用
LIMIT
只選取少數列,MySQL 在某些情況下會使用索引,而通常它會傾向於執行完整資料表掃描。如果您將
LIMIT
與row_count
ORDER BY
結合使用,MySQL 會在找到已排序結果的前row_count
列後立即停止排序,而不是對整個結果進行排序。如果排序是透過使用索引來完成,則速度非常快。如果必須執行檔案排序,則會選取符合不含LIMIT
子句的查詢的所有列,並在找到前row_count
列之前對它們的大部分或全部進行排序。在找到初始列後,MySQL 不會對結果集的任何剩餘部分進行排序。這種行為的一個表現是,具有和不具有
LIMIT
的ORDER BY
查詢可能會以不同的順序傳回列,如本節稍後所述。如果您將
LIMIT
與row_count
DISTINCT
結合使用,MySQL 會在找到row_count
個唯一列時立即停止。在某些情況下,可以透過依序讀取索引 (或對索引進行排序) 來解析
GROUP BY
,然後計算摘要直到索引值變更。在這種情況下,LIMIT
不會計算任何不必要的row_count
GROUP BY
值。一旦 MySQL 將所需的列數傳送到用戶端,它就會中止查詢,除非您使用
SQL_CALC_FOUND_ROWS
。在這種情況下,可以使用SELECT FOUND_ROWS()
檢索列數。請參閱第 14.15 節「資訊函式」。LIMIT 0
會快速回傳一個空集合。這對於檢查查詢的有效性很有用。它也可以用於在應用程式中使用 MySQL API 時,取得結果欄位的型別,這些 API 會提供結果集的中繼資料。使用 mysql 客戶端程式時,您可以使用--column-type-info
選項來顯示結果欄位的型別。如果伺服器使用暫存表格來解析查詢,它會使用
LIMIT
子句來計算需要多少空間。row_count
如果
ORDER BY
沒有使用索引,但同時存在LIMIT
子句,最佳化器可能會避免使用合併檔案,並在記憶體中使用記憶體內的filesort
操作來排序資料列。
如果多個資料列在 ORDER BY
欄位中具有相同的值,伺服器可以自由地以任何順序傳回這些資料列,並且可能會根據整體執行計畫以不同的方式執行。換句話說,這些資料列的排序順序對於未排序的欄位來說是不確定的。
影響執行計畫的一個因素是 LIMIT
,因此有和沒有 LIMIT
的 ORDER BY
查詢可能會以不同的順序傳回資料列。考慮以下查詢,它是按照 category
欄位排序的,但對於 id
和 rating
欄位來說是不確定的。
mysql> SELECT * FROM ratings ORDER BY category;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
| 1 | 1 | 4.5 |
| 5 | 1 | 3.2 |
| 3 | 2 | 3.7 |
| 4 | 2 | 3.5 |
| 6 | 2 | 3.5 |
| 2 | 3 | 5.0 |
| 7 | 3 | 2.7 |
+----+----------+--------+
包含 LIMIT
可能會影響每個 category
值內資料列的順序。例如,這是一個有效的查詢結果
mysql> SELECT * FROM ratings ORDER BY category LIMIT 5;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
| 1 | 1 | 4.5 |
| 5 | 1 | 3.2 |
| 4 | 2 | 3.5 |
| 3 | 2 | 3.7 |
| 6 | 2 | 3.5 |
+----+----------+--------+
在每種情況下,資料列都會按照 ORDER BY
欄位排序,這也是 SQL 標準要求的全部內容。
如果確保在有和沒有 LIMIT
的情況下,資料列的順序都相同是很重要的,請在 ORDER BY
子句中包含額外的欄位,以使順序是確定的。例如,如果 id
值是唯一的,您可以透過像這樣排序,使給定 category
值的資料列以 id
順序顯示
mysql> SELECT * FROM ratings ORDER BY category, id;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
| 1 | 1 | 4.5 |
| 5 | 1 | 3.2 |
| 3 | 2 | 3.7 |
| 4 | 2 | 3.5 |
| 6 | 2 | 3.5 |
| 2 | 3 | 5.0 |
| 7 | 3 | 2.7 |
+----+----------+--------+
mysql> SELECT * FROM ratings ORDER BY category, id LIMIT 5;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
| 1 | 1 | 4.5 |
| 5 | 1 | 3.2 |
| 3 | 2 | 3.7 |
| 4 | 2 | 3.5 |
| 6 | 2 | 3.5 |
+----+----------+--------+
對於具有 ORDER BY
或 GROUP BY
和 LIMIT
子句的查詢,最佳化器預設會嘗試選擇一個排序索引,當這樣做似乎可以加快查詢執行速度時。在某些情況下,使用其他最佳化可能更快,可以透過將 optimizer_switch
系統變數的 prefer_ordering_index
旗標設定為 off
來關閉此最佳化。
範例:首先,我們建立並填入一個表格 t
,如下所示
# Create and populate a table t:
mysql> CREATE TABLE t (
-> id1 BIGINT NOT NULL,
-> id2 BIGINT NOT NULL,
-> c1 VARCHAR(50) NOT NULL,
-> c2 VARCHAR(50) NOT NULL,
-> PRIMARY KEY (id1),
-> INDEX i (id2, c1)
-> );
# [Insert some rows into table t - not shown]
驗證 prefer_ordering_index
旗標已啟用
mysql> SELECT @@optimizer_switch LIKE '%prefer_ordering_index=on%';
+------------------------------------------------------+
| @@optimizer_switch LIKE '%prefer_ordering_index=on%' |
+------------------------------------------------------+
| 1 |
+------------------------------------------------------+
由於以下查詢具有 LIMIT
子句,我們預期它會盡可能使用排序索引。在這種情況下,正如我們從 EXPLAIN
輸出中看到的那樣,它使用了表格的主鍵。
mysql> EXPLAIN SELECT c2 FROM t
-> WHERE id2 > 3
-> ORDER BY id1 ASC LIMIT 2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t
partitions: NULL
type: index
possible_keys: i
key: PRIMARY
key_len: 8
ref: NULL
rows: 2
filtered: 70.00
Extra: Using where
現在我們停用 prefer_ordering_index
旗標,並重新執行相同的查詢;這次它使用索引 i
(其中包含 WHERE
子句中使用的 id2
欄位)和 filesort
mysql> SET optimizer_switch = "prefer_ordering_index=off";
mysql> EXPLAIN SELECT c2 FROM t
-> WHERE id2 > 3
-> ORDER BY id1 ASC LIMIT 2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t
partitions: NULL
type: range
possible_keys: i
key: i
key_len: 8
ref: NULL
rows: 14
filtered: 100.00
Extra: Using index condition; Using filesort
另請參閱 第 10.9.2 節,「可切換的最佳化」。