SQL Server UNION - 默认的 ORDER BY 行为是什么?

41

如果我有几个UNION语句,以下是一个人为的例子:

SELECT * FROM xxx WHERE z = 1
UNION 
SELECT * FROM xxx WHERE z = 2
UNION
SELECT * FROM xxx WHERE z = 3

默认的order by行为是什么?

我看到的测试数据基本上没有按照指定的顺序返回数据。也就是说,数据已经排序,但我想知道这种排序的优先级规则是什么。

另一件事是,在这种情况下xxx是一个视图。该视图将3个不同的表连接在一起以返回我想要的结果。

6个回答

49

没有默认排序。

如果没有 Order By 子句,返回的顺序是未定义的。这意味着 SQL Server 可以按照任何它喜欢的顺序返回结果。

编辑: 根据我所看到的,如果没有 Order By,结果返回的顺序取决于查询计划。因此,如果使用了索引,则结果可能按照该顺序返回,但仍然不能保证。


我问的原因是因为有一个明确的顺序,我猜测基于某个阶段的选择操作中有一个聚集索引(一个主键)。 - Ray Booysen
2
无论多个连续的选择返回什么顺序,都可能是一致的。但您不应该依赖于顺序与任何事物具有偶然关系。添加表格、索引或更多数据可能会影响顺序。不要成为懒惰的编码者。 - DrFloyd5
1
正如DJ所指出的,如果没有ORDER BY子句,查询每次运行时甚至可能会有所不同。 - BradC
是的,我意识到这一点,但我正在寻找一个明确的答案,关于"order by"的行为是什么。 - Ray Booysen
相同的行为是否也适用于Postgres或一般的SQL? - Harsh Gundecha

16

关于添加 ORDER BY 子句:

对于大多数人来说,这可能很基础,但我想补充一点。 有时您不希望结果混合,因此您希望首先返回第一个查询的结果,然后是第二个查询的结果,以此类推。为了做到这一点,我只需添加一个虚拟的第一列并按照它进行排序。由于在联接中可能会忘记给列命名别名而导致问题,因此我通常在 ORDER BY 子句中使用序号而不是列名。

例如:

SELECT 1, * FROM xxx WHERE z = 'abc'
UNION ALL
SELECT 2, * FROM xxx WHERE z = 'def'
UNION ALL
SELECT 3, * FROM xxx WHERE z = 'ghi'
ORDER BY 1

虚拟序号列在我运行两个查询并且只有一个会返回结果时也很有用。因此,我只需检查返回结果的序号即可。这样可以避免进行多次数据库调用和大部分无结果集检查。


13

刚刚找到了确切的答案。

因为UNION操作会去除重复值,所以它会进行DISTINCT SORT排序。这个排序是在所有UNION语句拼接之前完成的(请查看执行计划)。

如果想要停止排序,请使用UNION ALL语句,这样也不会删除重复值。


5
这并没有真正回答问题:如果使用“union all”,顺序是否仍然未定义?(请提供参考资料) - Matt Fenwick
我不确定这是否是技术上正确的答案,但在SQLite中,“UNION ALL”保留了用户呈现查询的顺序,而不是在后端执行排序。这个答案特别解决了我的问题。 - StonedTensor

8

如果您关心记录的返回顺序,必须使用order by。

如果您省略它,结果可能会看起来有组织(基于查询计划选择的索引),但今天看到的结果可能不是您期望的结果,甚至在明天运行相同的查询时也可能发生变化。

编辑:以下是一些好的、具体的例子:(所有例子均为MS SQL Server)

  • Dave Pinal's blog describes how two very similar queries can show a different apparent order, because different indexes are used:

    SELECT ContactID FROM Person.Contact
    SELECT *         FROM Person.Contact
    
  • Conor Cunningham shows how the apparent order can change when the table gets larger (if the query optimizer decides to use a parallel execution plan).

  • Hugo Kornelis proves that the apparent order is not always based on primary key. Here is his follow-up post with explanation.


1
如果一个查询是并行的,你可以得到随机的数据块,每个数据块都是排序过的,但是这些数据块是无序的;而且有很多特殊情况(例如:在查询运行时发生页面分裂),这意味着即使你的查询计划看起来按顺序给出了结果(即没有其他影响操作的顺序),你也可能偶尔会得到乱序的聚集索引行。服务器甚至可以先把当前缓存中的10页数据提供给你,然后再回去扫描剩下的数据,跳过已经给你的页面。 - Andrew Hill

2

关于结果集排序,UNION可能会产生误导,因为数据库有时会使用一种排序方法来提供隐含在UNION中的DISTINCT,这使得行看起来是有意排序的——当然,对于没有隐含去重的UNION ALL,则不适用此规则。

然而,对于隐含去重,例如Oracle 10g+中的哈希方法,将不会应用任何排序。

正如DJ所说,始终使用ORDER BY。


是的,我并不依赖于 order by,只是对其行为感兴趣。 - Ray Booysen

1

很常见的情况是遇到编写不良的代码,假定表格中的数据以插入顺序返回,95% 的情况下编码人员可以逃脱这个问题,而且在许多常用数据库(MSSQL、Oracle、MySQL)上也不知道这是个问题。当然,这是一个完全错误的想法,在发现它时应该始终进行矫正,并且总是必须自己使用 Order By 子句。


是的,我意识到了这一点,只是在寻找明确的行为。 - Ray Booysen
4
没有order by子句,任何查询的顺序都是“未定义的”,这就是关于各种SQL标准的规定。因为明确地声明了“没有规定性行为”,所以不存在明确的行为。 - Cruachan

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