文件首頁
MySQL 9.0 參考手冊
相關文件 下載本手冊
PDF (US Ltr) - 40.0Mb
PDF (A4) - 40.1Mb
Man Pages (TGZ) - 258.2Kb
Man Pages (Zip) - 365.3Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 9.0 參考手冊  /  ...  /  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 時,取得結果欄位的型別,這些 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 BYLIMIT 子句的查詢,最佳化器預設會嘗試選擇一個排序索引,當這樣做似乎可以加快查詢執行速度時。在某些情況下,使用其他最佳化可能更快,可以透過將 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 節,「可切換的最佳化」