#### 8.2.1.19 LIMIT 查询优化

[](<>)[](<>)

如果您只需要结果集中指定数量的行，请使用`限制`子句，而不是获取整个结果集并丢弃额外的数据。

MySQL 有时会优化具有`限制 *`行数`*`条款和没有`拥有`条款：

-   如果您只选择几行`限制`, MySQL 在某些情况下使用索引，而通常它更愿意进行全表扫描。

-   如果你结合`限制 *`行数`*`和`订购方式`, MySQL 一找到第一个就停止排序*`行数`*排序结果的行，而不是对整个结果进行排序。如果使用索引进行排序，则速度非常快。如果必须进行文件排序，则与查询匹配的所有行都没有`限制`子句被选中，并且大部分或全部被排序，在第一个之前*`行数`*被发现。在找到初始行之后，MySQL 不对结果集的任何剩余部分进行排序。

    这种行为的一个表现是`订购方式`有无查询`限制`可能以不同的顺序返回行，如本节后面所述。

-   如果你结合`限制 *`行数`*`和`清楚的`, MySQL 一发现就停止*`行数`*唯一的行。

-   在某些情况下，一个`通过...分组`可以通过按顺序读取索引（或对索引进行排序），然后计算摘要直到索引值发生变化来解决。在这种情况下，`限制 *`行数`*`不计算任何不必要的`通过...分组`价值观。

-   一旦 MySQL 向客户端发送了所需的行数，它就会中止查询，除非您正在使用`SQL_CALC_FOUND_ROWS`.在这种情况下，可以使用检索行数`选择 FOUND_ROWS()`.看[第 12.16 节，“信息功能”](information-functions.html).

    [](<>)

-   `限制 0`快速返回一个空集。这对于检查查询的有效性很有用。它还可以用于获取使用 MySQL API 的应用程序中结果列的类型，该 API 使结果集元数据可用。随着[**mysql**](mysql.html)客户端程序，您可以使用[`--列类型信息`](mysql-command-options.html#option_mysql_column-type-info)显示结果列类型的选项。

-   如果服务器使用临时表来解析查询，它使用`限制 *`行数`*`子句来计算需要多少空间。

-   如果索引不用于`订购方式`但是一个`限制`子句也存在，优化器可能能够避免使用合并文件并使用内存中的行对内存中的行进行排序`文件排序`手术。

    如果多行在`订购方式`列，服务器可以自由地以任何顺序返回这些行，并且可能会根据整体执行计划以不同的方式返回。换句话说，这些行的排序顺序对于无序的列是不确定的。

    影响执行计划的一个因素是`限制`, 所以一个`订购方式`有无查询`限制`可能以不同的顺序返回行。考虑这个查询，它按`类别`列但关于`id`和`评分`列：


```
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 |
+----+----------+--------+
```

包含`限制`可能会影响每个内的行顺序`类别`价值。例如，这是一个有效的查询结果：

```
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 |
+----+----------+--------+
```

在每种情况下，行都按`订购方式`列，这是 SQL 标准所要求的全部内容。

如果确保有和没有相同的行顺序很重要`限制`, 在`订购方式`子句使订单具有确定性。例如，如果`id`值是唯一的，您可以为给定的行`类别`值出现在`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 |
+----+----------+--------+
```

对于一个查询`订购方式`要么`通过...分组`和一个`限制`子句时，优化器会在默认情况下尝试选择有序索引，这样做会加快查询执行速度。在 MySQL 8.0.21 之前，无法覆盖此行为，即使在使用其他优化可能更快的情况下也是如此。从 MySQL 8.0.21 开始，可以通过设置[`优化器开关`](server-system-variables.html#sysvar_optimizer_switch)系统变量[`prefer_ordering_index`](switchable-optimizations.html#optflag_prefer-ordering-index)标记为`离开`.

*例子*：首先我们创建并填充一个表`吨`如此处所示：

```
# 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`](switchable-optimizations.html#optflag_prefer-ordering-index)标志已启用：

```
mysql> SELECT @@optimizer_switch LIKE '%prefer_ordering_index=on%';
+------------------------------------------------------+
| @@optimizer_switch LIKE '%prefer_ordering_index=on%' |
+------------------------------------------------------+
|                                                    1 |
+------------------------------------------------------+
```

由于以下查询具有`限制`如果可能，我们希望它使用有序索引。在这种情况下，我们可以从[`解释`](explain.html)输出，它使用表的主键。

```
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`](switchable-optimizations.html#optflag_prefer-ordering-index)标记，然后重新运行相同的查询；这次它使用索引`一世`（其中包括`id2`中使用的列`在哪里`子句）和文件排序：

```
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
```

也可以看看[第 8.9.2 节，“可切换的优化”](switchable-optimizations.html).
