SQL Select语句没有Order By子句的顺序

68
据我所知,从关系数据库理论上讲,一个没有order by子句的select语句应被视为没有特定的排序。但实际上在SQL Server和Oracle中(我已在这两个平台上进行了测试),如果我从一个没有order by子句的表中多次查询,我总是会以相同的顺序获得结果。这种行为可靠吗?有人能帮忙解释一下吗?

4
不,它不能被信赖。在SQL Server中,如果您没有指定顺序,那么您可能会得到索引扫描或分配有序扫描等结果。此外,您还可能遇到“高级扫描”/旋转木马扫描功能。 - Martin Smith
6
添加一个ORDER BY子句。就是这样。 - Remus Rusanu
10
是的,它是可靠的。但是在最不方便的时候,它可能不能按照你之前所依赖的方式工作 :-) - DCookie
2
如果您知道您想要和期望的顺序,那么略去ORDER BY有什么意义呢?这些按键真的值得为可预测性而交换吗? - Aaron Bertrand
4个回答

75
不,不能依靠那种行为。排序方式是由查询规划器决定如何组建结果集来决定的。像select * from foo_table这样简单的查询可能会按照存储在磁盘上的顺序返回,可能是按照主键顺序或创建顺序或其他随机顺序。更复杂的查询,比如 select * from foo where bar < 10,可能根据索引读取以不同列的顺序返回,或者根据表的顺序返回用于表扫描。更加复杂的查询,包括多个where条件、group by子句、union操作,将按照规划器确定的最有效生成顺序生成。
甚至两个相同的查询之间的顺序也可能发生改变,仅因为这些查询之间的数据发生了变化。一个"where"子句可以在一个查询中通过索引扫描得到满足,但后续的插入操作可能会使这个条件变得不太具有选择性,规划器可能会决定使用表扫描执行后续查询。
更准确地说,关系型数据库管理系统的任务是尽可能高效地给你提供你所要求的内容。这种效率可以采用许多形式,包括最小化IO(在磁盘和网络上发送数据到你),最小化CPU并保持其工作集的大小小(使用需要最少临时存储的方法)。
如果没有ORDER BY子句,你就没有准确地要求特定的顺序,因此RDBMS将按照RDBMS预计能够最快生成数据的算法给你返回这些行,并以某种偶然的查询方面对应的方式排序。
如果您关心效率而不关心排序,请跳过ORDER BY子句。如果您关心顺序而不关心效率,请使用ORDER BY子句。

如果你真的关心两个方面,就使用ORDER BY,并仔细调整你的查询和数据库,使其更加高效。


始终包括 ORDER BY 的另一个原因是 SQL Server Enterprise Advanced Scan,也称为 Merry-Go-Round Scan。 (在 Dmitri Korotkevitch 的优秀著作《Pro SQL Server Internals》中提到) - Grzegorz Smulko

11
不可以仅依赖获取结果的顺序是一样的。 当我在处理一个分页网格的网页时,我发现了这一点。当我跳到下一页再回到上一页时,上一页会包含不同的记录!我非常困惑。

为了获得可预测的结果,应该使用ORDER BY即使如此,在指定列中有相同的值时,你也可能会得到不同的结果。你可能需要按照你并不认为必要的字段来排序,以获得可预测的结果。


4

Tom Kyte对这个主题有一个小抱怨。不知道为什么,人们对此很着迷,并试图提出一些情况,在这些情况下,您可以依靠特定的顺序而不必指定ORDER BY。正如其他人所述,您不能这样做。这是AskTom网站上另一个有趣的帖子


1

正确的答案

这是一个新的回答,用来纠正旧的回答。我从Tom Kyte那里得到了答案,并在此发布:

如果您想要对行进行排序,必须使用ORDER BY。没有任何条件或者例外,绝对如此。http://tkyte.blogspot.ru/2005/08/order-in-court.html 您需要在IOT上使用order by。行在叶块中排序,但是叶块不按排序存储。快速完整扫描=未排序的行。

https://twitter.com/oracleasktom/status/625318150590980097

https://twitter.com/oracleasktom/status/625316875338149888


错误答案

(注意!以下原问题的答案仅用于历史记录,是错误的答案。正确答案在上面)

正如Tom Kyte在之前提到的文章中所写:

您应该将堆有序表视为一个大的无序行集合。这些行将以看似随机的顺序出现,并且取决于使用的其他选项(并行查询、不同的优化器模式等),它们可能在相同的查询下以不同的顺序出现。除非您在查询中有ORDER BY语句,否则永远不要指望从查询中获取行的顺序!

但请注意,他只谈到了堆有序表。但也有索引有序表。在这种情况下,您可以依赖选择的顺序而不需要ORDER BY,因为顺序是由主键隐式定义的。这对于Oracle是正确的。

对于SQL Server聚集索引(索引有序表)默认创建。还有可能让PostgreSQL通过索引对齐存储信息。更多信息可以在这里找到。

更新:我发现我的答案被投票否决了。所以我会尝试解释一下我的观点。 在索引有序表概述部分,有这样一句话:

在索引有序表中,行存储在定义在表的主键上的索引中... 当相关数据必须一起存储或数据必须按特定顺序物理存储时,索引有序表非常有用。

http://docs.oracle.com/cd/E25054_01/server.1111/e25789/indexiot.htm#CBBJEBIH

由于索引的存在,所有数据都按特定顺序存储,我相信Pg也是如此。 http://www.postgresql.org/docs/9.2/static/sql-cluster.html 如果您不同意我的看法,请给我提供文档链接。我很乐意知道有什么可以学习的。

1
你能否指出在Oracle或Postgres文档中有哪些说明,即使没有“ORDER BY”子句,这些存储类型的选择顺序也是确定的?我相当确定没有这样的承诺存在。 - SingleNegationElimination
@SingleNegationElimination 我引用了Oracle文档中的一句话。很遗憾看到我的回答被投票否决了... - Alexander Myshov
我也在Twitter上向Tom Kyte询问了这个问题,希望他能回答并在我得到答案后发布链接。 - Alexander Myshov
不要忘记次要索引。访问IOT的方式不止一种。 - Jon Heller
我认为你混淆了存储行的顺序和查询返回的行的保证顺序。这不是同一件事情。 - Mikael Eriksson
3
你们都是对的。我有点困惑 :| Tom Kyte已经回答了我的问题。我把Tom的答案放在了我的答案顶部。但是出于历史记录的考虑,我不想删除我的原始答案。所以我也把它留在这里,并加上“错误答案”的标题。也许我的错误会在某个时候帮助到别人。 - Alexander Myshov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接