如果您只需要结果集中的指定行数,请在查询中使用 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
列)和文件排序
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 节,“可切换优化”。