文件首頁
MySQL 8.4 參考手冊
相關文件 下載本手冊
PDF (美式信紙) - 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 參考手冊  /  ...  /  LIMIT 查詢最佳化

10.2.1.19 LIMIT 查詢最佳化

如果您只需要結果集中指定數量的列,請在查詢中使用 LIMIT 子句,而不是擷取整個結果集並丟棄多餘的資料。

MySQL 有時會最佳化具有 LIMIT row_count 子句且沒有 HAVING 子句的查詢

  • 如果您僅使用 LIMIT 選取少數幾列,MySQL 在某些情況下會使用索引,而通常它會偏好執行完整資料表掃描。

  • 如果您將 LIMIT row_countORDER BY 結合使用,MySQL 會在找到排序結果的前 row_count 列時立即停止排序,而不是排序整個結果。如果排序是透過使用索引來完成的,則速度會非常快。如果必須執行檔案排序,則會選取與沒有 LIMIT 子句的查詢相符的所有列,並且在找到前 row_count 列之前,會排序大部分或全部列。在找到初始列之後,MySQL 不會排序結果集的任何剩餘部分。

    此行為的一種表現形式是,具有和不具有 LIMITORDER BY 查詢可能會以不同的順序傳回列,如本節稍後所述。

  • 如果您將 LIMIT row_countDISTINCT 結合使用,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,因此具有和不具有 LIMITORDER BY 查詢可能會以不同的順序傳回列。請考慮這個查詢,它按 category 欄位排序,但相對於 idrating 欄位是不確定的

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 BYGROUP 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 節「可切換的最佳化」