如果您只需要結果集中指定數量的列,請在查詢中使用 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 來提供結果集的中繼資料。使用 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 節「可切換的最佳化」。