MySQL中SELECT语句的默认记录顺序是什么?


66

假设您具有以下表和数据:

create table t (
    k int,
    v int,
    index k(k)
    ) engine=memory;

insert into t (k, v)
values (10, 1),
       (10, 2),
       (10, 3);

在发出select * from t where k = 10no order by子句时,默认情况下MySQL如何对记录排序?

Answers:


75

重新发布我的回答到关于SQL Server的一个类似的问题:

在SQL世界中,顺序不是一组数据的固有属性。因此,除非您使用ORDER BY子句查询数据,否则RDBMS不能保证您的数据将以特定顺序返回,甚至不能以一致的顺序返回。

因此,回答您的问题:

  • MySQL会对记录进行排序,但是不保证一致性。
  • 如果您打算依赖此订单执行任何操作,则必须使用来指定所需的订单ORDER BY。做其他事情就是让自己为不受欢迎的惊喜做好准备。

这是所有SQL的属性,而不仅仅是MySQL。SQL-92规范中的相关文本为:

如果未指定<order by子句>,则Q行的顺序与实现有关。

游标规范中有类似的文字内容。


26

没有ORDER BY子句时的行顺序可以是:

  • 任何两个存储引擎之间都不同;
  • 如果使用相同的存储引擎,则同一存储引擎的任何两个版本之间可能会有所不同;此处的示例,向下滚动到“行的排序”。
  • 如果存储引擎版本相同,但MySQL版本不同,则可能由于查询优化程序在这些版本之间的更改而有所不同;
  • 如果一切都相同,则由于月相的不同而不同,没关系。

10

到达时插入无序,混乱。创建的索引确实具有顺序,其中元素是在链接列表(即索引)中的正确位置插入的。考虑一个索引的三重链表,其中有一个从一个索引元素到下一个索引元素的前向链接,一个用于遍历和完整性目的的向后链接,然后是指向表中实际记录的一组指针,其中与相关索引元素匹配。

实际数据,存储混乱。与数据关联的索引,在存储和构造中排序。数据的实际拉取(有序或无序)取决于所涉及的查询。


4

对于MEMORY存储引擎,我希望该顺序按插入顺序进行,因为默认索引布局是HASH代替的,BTREE并且索引布局的任何方面都没有使用。由于您索引了k,并且k是相同的值,因此所有键都输入相同的哈希存储桶。由于没有理由假设填充散列桶会增加复杂性,因此插入顺序最有意义。

我使用了相同的样本表和数据,并运行了30 INSERT秒,得到了:

mysql> use test
Database changed
mysql> drop table if exists t;
Query OK, 0 rows affected (0.00 sec)

mysql> create table t(k int, v int,index k(k)) engine=memory;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t values
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3),
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3),
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3),
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3),
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3);
Query OK, 30 rows affected (0.00 sec)
Records: 30  Duplicates: 0  Warnings: 0

mysql> select * from t;
+------+------+
| k    | v    |
+------+------+
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
+------+------+
30 rows in set (0.00 sec)

mysql>

我决定测试为k添加两个不同的值:10和11我得到了:

mysql> use test
Database changed
mysql> drop table if exists t;
Query OK, 0 rows affected (0.02 sec)

mysql> create table t(k int, v int,index k(k)) engine=memory;
Query OK, 0 rows affected (0.01 sec)

mysql> insert into t values
    -> (11, 1), (11, 2), (11, 3), (10, 1), (10, 2), (10, 3),
    -> (11, 1), (11, 2), (11, 3), (10, 1), (10, 2), (10, 3),
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3),
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3),
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3);
Query OK, 30 rows affected (0.00 sec)
Records: 30  Duplicates: 0  Warnings: 0

mysql> select * from t;
+------+------+
| k    | v    |
+------+------+
|   11 |    1 |
|   11 |    2 |
|   11 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   11 |    1 |
|   11 |    2 |
|   11 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
+------+------+
30 rows in set (0.00 sec)

mysql>

看起来像插入顺序。k = 11是第一个密钥,然后是10,然后散列10。那么首先插入10而不是11怎么办?这就是我得到的:

mysql> use test
Database changed
mysql> drop table if exists t;
Query OK, 0 rows affected (0.02 sec)

mysql> create table t(k int, v int,index k(k)) engine=memory;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t values
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3),
    -> (11, 1), (11, 2), (11, 3), (10, 1), (10, 2), (10, 3),
    -> (11, 1), (11, 2), (11, 3), (10, 1), (10, 2), (10, 3),
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3),
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3);
Query OK, 30 rows affected (0.00 sec)
Records: 30  Duplicates: 0  Warnings: 0

mysql> select * from t;
+------+------+
| k    | v    |
+------+------+
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   11 |    1 |
|   11 |    2 |
|   11 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   11 |    1 |
|   11 |    2 |
|   11 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
+------+------+
30 rows in set (0.00 sec)

mysql>

这是一致的!插入顺序是答案。

关于将索引用于MEMORY存储引擎的旁注

对于MEMORY的范围搜索将具有相当恐怖的性能。

创建索引时,可以指定USING BTREE子句以及索引的定义。这将改善范围查询的情况。

搜索特定的行会产生与两种性能相同的结果HASHBTREE

更新2011-09-22 11:18 EDT

今天我学到了一些有趣的东西。我阅读了Percona 的@Laurynas Biveinis提供的链接:Percona链接说明了有关MySQL 5.5.15的 MEMORY表的内容

行的排序

在没有ORDER BY的情况下,记录可能以与以前的MEMORY实现不同的顺序返回。这不是错误。任何依赖特定顺序而不带ORDER BY子句的应用程序都可能传递意外结果。不带ORDER BY的特定顺序是存储引擎和查询优化器实现的副作用,在次要的MySQL版本之间可能会改变。

这是我今天看到的一个很好的链接。我给出的答案表明,已加载的表已按我今天在MySQL 5.5.12中预期的顺序进行检索。正如Percona和@Laurynas Biveinis所指出的,不能保证另一个次要版本。

因此,我宁愿推荐@Laurynas Biveinis的答案,也不愿捍卫我的答案,因为它是最新信息。荣誉和帽子关闭@Laurynas Biveinis。我还要感谢@eevar礼貌地指出不要推广特定于版本的问题答案。他们俩今天都得到我的支持。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.