【问题标题】:What is MySQL row order for "SELECT * FROM table_name;"?“SELECT * FROM table_name;”的 MySQL 行顺序是什么?
【发布时间】:2010-12-29 07:26:17
【问题描述】:

假设向 MySQL 数据库发出以下查询:

SELECT * FROM table_name;

请注意,no ORDER BY 子句已给出。

我的问题是:

MySQL 是否保证结果集行的顺序?

更具体地说,我可以假设行将按插入顺序返回吗?这与将行插入表中的顺序相同。

【问题讨论】:

标签: mysql select sql-order-by


【解决方案1】:

不,没有任何保证。除非您使用ORDER BY 子句指定顺序,否则顺序完全取决于内部实现细节。 IE。任何对 RDBMS 引擎最方便的东西。

实际上,行可能以其原始插入顺序(或更准确地说是行在物理存储中存在的顺序)返回,但您不应依赖此。如果您将应用移植到另一个品牌的 RDBMS,或者即使您升级到可能以不同方式实现存储的更新版本的 MySQL,这些行可能会以其他顺序返回。

后一点适用于任何符合 SQL 的 RDBMS。


下面是我所说的行在存储中存在的顺序与它们被创建的顺序的演示:

CREATE TABLE foo (id SERIAL PRIMARY KEY, bar CHAR(10));

-- create rows with id 1 through 10
INSERT INTO foo (bar) VALUES
  ('testing'), ('testing'), ('testing'), ('testing'), ('testing'), 
  ('testing'), ('testing'), ('testing'), ('testing'), ('testing');

DELETE FROM foo WHERE id BETWEEN 4 AND 7;

+----+---------+
| id | bar     |
+----+---------+
|  1 | testing |
|  2 | testing |
|  3 | testing |
|  8 | testing |
|  9 | testing |
| 10 | testing |
+----+---------+

所以现在我们有六行。此时的存储包含第 3 行和第 8 行之间的间隙,在删除中间行后留下。删除行不会对这些间隙进行碎片整理。

-- create rows with id 11 through 20 
INSERT INTO foo (bar) VALUES
  ('testing'), ('testing'), ('testing'), ('testing'), ('testing'), 
  ('testing'), ('testing'), ('testing'), ('testing'), ('testing');

SELECT * FROM foo;

+----+---------+
| id | bar     |
+----+---------+
|  1 | testing |
|  2 | testing |
|  3 | testing |
| 14 | testing |
| 13 | testing |
| 12 | testing |
| 11 | testing |
|  8 | testing |
|  9 | testing |
| 10 | testing |
| 15 | testing |
| 16 | testing |
| 17 | testing |
| 18 | testing |
| 19 | testing |
| 20 | testing |
+----+---------+

请注意 MySQL 如何在将新行附加到表末尾之前重新使用通过删除行打开的空间。另请注意,第 11 行到第 14 行以相反的顺序插入到这些空间中,从末尾向后填充。

因此,行的存储顺序并不完全是它们插入的顺序。


更新:我在 2009 年写的这个演示是为 MyISAM 编写的。除非您使用 ORDER BY,否则 InnoDB 按索引顺序返回行。这进一步证明了答案开头的点,即默认顺序取决于实现。使用不同的存储引擎意味着不同的实现。

【讨论】:

  • 你的 MySQL 是什么版本。我的 mysql DBMS 版本是 5.6.16,适用于 osx10.9 (x86_64)。结果和你的很不一样。
  • @wanghao,我在 2009 年写了这个答案,当时默认的存储引擎是 MyISAM。在 MySQL 5.6 中,默认的存储引擎是 InnoDB,它总是按主键顺序存储行。尝试使用 MyISAM 表进行实验。
  • @BillKarwin,然后我使用 MyISAM,它可以工作,谢谢!
  • @ButtleButkus,数据是按照它用来遍历数据的索引的顺序返回的。请参阅 EXPLAIN 以了解它用于第一个表的索引。
【解决方案2】:

默认情况下,mysql 5.6 中的结果不按插入顺序选择,您可以进行更新等,它会以它喜欢的任何顺序返回查询

【讨论】:

    【解决方案3】:

    不,你不能。

    有时 MySQL 会使用您不期望的键执行选择查询。考虑这张表:

    CREATE TABLE `user_permissions` (
      `permId` int(5) unsigned NOT NULL AUTO_INCREMENT,
      `permKey` varchar(16) NOT NULL,
      `permDesc` varchar(64) DEFAULT NULL,
      PRIMARY KEY (`permId`),
      KEY `key_lookup` (`permKey`,`permId`,`permDesc`)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
    

    MySQL 几乎总是使用key_lookup 键对该表进行任何选择操作;因为permKey 是第一个字段,它通常会按此键“排序”(按字母顺序显示,但不完全是)。

    如果没有ORDER BY 子句,MySQL(以及大多数/所有 RDBMS 引擎)将尝试尽可能快地获取数据,就像它存储一样。

    【讨论】:

      【解决方案4】:

      来自Retrieving Data Using the MySQL SELECT Statement : The SELECT Statement

      显示的数据没有排序。 通常在 插入它们的顺序相同 进入数据库

      是的,根据评论

      虽然记录通常是 按他们的顺序检索 被插入数据库,你 不能依赖特定的顺序 被保存。如果你的数据库是 备份和恢复,或者如果 维护操作在 数据库,MySQL 可能会改变 记录的存储顺序 内部。

      【讨论】:

      • Annnnd... 紧接着:“虽然记录通常是按照它们插入数据库的顺序检索的,但您不能依赖于保留的特定顺序。”您永远不能依赖它,因为一旦数据页已满,行就会四处移动。
      • 是的,我同意这样一个事实,即如果没有 orderby,您将无法确定您会得到什么。
      【解决方案5】:

      不,绝对不是。根据您使用的数据库引擎(ISAM 或 InnoDB),表结构将generally be some kind of b-tree 允许更快地搜索行。这与插入顺序无关,更多的是与数据库如何基于主键构建索引(或者在没有键的情况下,如何存储表堆)有关。

      【讨论】:

        【解决方案6】:

        根据this thread,默认排序是 MyISAM 的插入顺序,而 InnoDB 的主键升序。但我不认为这是保证,只是已知它是如何工作的。

        【讨论】:

        • 如果您在这些表中进行删除和插入,结果肯定不会出现在这些顺序中。 @BillKarwin 很好地解释了上述原因。
        猜你喜欢
        • 1970-01-01
        • 2011-01-17
        • 2012-03-16
        • 1970-01-01
        • 2011-03-22
        • 1970-01-01
        • 2018-04-12
        • 2013-06-01
        相关资源
        最近更新 更多