是否真的有可能无法保证针对此特定的冗余派生表的顺序?


12

与Lukas EderTwitter交谈中,我偶然发现了这个问题。

尽管正确的行为是将ORDER BY子句应用于最外层查询,因为在这里,我们没有在最外层查询中使用DISTINCT,GROUP BY,JOIN或任何其他WHERE子句,因此RDBMS为什么不通过传入的数据是否由内部查询排序?

SELECT * 
FROM (
    SELECT * FROM table ORDER BY time DESC
) AS t

至少在PostgreSQL上运行此示例时,对于内部查询和此派生表示例,您将获得相同的执行计划,以及相同的结果集。

因此,我认为规划器将只丢弃最外面的查询,因为它是多余的,或者只是传递内部表的结果。

有人认为情况可能并非如此吗?


4
请注意,您的查询将在SQL Server中失败,因为派生表中不允许使用order by。
a_horse_with_no_name

你怎么这么不信 你为什么要承担任何责任?当您编写一个让您选择的程序时,您是否希望用户对您的选择有所期待?了解有关逻辑和物理查询的优化/实现。
philipxy

2
“我认为规划器将只丢弃最外面的查询,因为它是多余的,或者只是传递内部表的结果。” 您可以轻松地假设Planner会丢弃内部查询上的ordering子句,因为它在上下文中毫无意义。
通配符

关于2012年的MariaDB讨论了这个问题。内在的缺乏ORDER BY导致分组最大的不同优化
瑞克·詹姆斯

1
实际上,您适合使用Postgres。
Erwin Brandstetter

Answers:


20

大多数数据库都非常清楚以下事实:ORDER BY子查询中的an 是:

  • 不允许:例如SQL Server,Sybase SQL Anywhere(除非以TOP或补充OFFSET .. FETCH
  • 毫无意义:例如PostgreSQL,DB2(再次,除非用OFFSET .. FETCH或补充LIMIT

这是DB2 LUW手册中的一个示例(重点是我的)

子选择中的ORDER BY子句不会影响查询返回的行的顺序。如果在最外面的全查询中指定了ORDER BY子句,则仅影响返回的行的顺序。

措辞非常明确,就像PostgreSQL的一样

如果未选择排序,则将以未指定的顺序返回行。在这种情况下,实际顺序将取决于扫描和联接计划的类型以及磁盘上的顺序,但一定不能依赖它。只有明确选择了排序步骤,才能保证特定的输出顺序。

根据此规范,可以得出结论ORDER BY,派生表中的子句所产生的任何排序都是偶然的,并且可能恰好符合您的预期排序(在您的琐碎示例中,大多数数据库都这样做),但是依靠它是不明智的这个。

关于DB2的旁注:

特别是,DB2具有一个鲜为人知的功能,称为ORDER BY ORDER OF <table-designator>,可以按如下方式使用:

SELECT C1 FROM
   (SELECT C1 FROM T1
      UNION
    SELECT C1 FROM T2
    ORDER BY C1 ) AS UTABLE
ORDER BY ORDER OF UTABLE

在这种特殊情况下,派生表的顺序可以在最外面的SELECT中显式重用

关于Oracle的旁注:

多年来,Oracle一直在实践中OFFSET使用分页来实现分页ROWNUM,只有对派生表进行排序之后才能合理地计算分页:

SELECT *
FROM (
  SELECT rownum AS rn, t.* -- ROWNUM here depends on the derived table's ordering
  FROM (
    SELECT * FROM table ORDER BY time DESC
  ) t
) t
WHERE rn BETWEEN 10 AND 20

可以合理地预期,至少在存在ROWNUM查询的情况下,将来的Oracle版本不会破坏此行为,以便不会破坏几乎所有的旧版Oracle SQL,而这些旧版SQL尚未迁移到更理想的版本。可读的SQL标准OFFSET .. FETCH语法:

SELECT * FROM table ORDER BY time DESC OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY

Meaningless: E.g. PostgreSQL应该真的是:“不可靠”,因为它确实意味着某些东西。在内部查询中对行进行排序,并且除非另有说明,否则顺序将在外部查询级别中保留,否则重新排序对于其他操作是适当的。即使那只是实现细节,也不是没有意义的。这可用于排序输入以聚合函数。该手册甚至暗示了很多: Alternatively, supplying the input values from a sorted subquery will usually work.
Erwin Brandstetter

您为Postgres添加的报价实际上适用于另一种情况:根本没有查询ORDER BY
Erwin Brandstetter

@ErwinBrandstetter:随意添加这些细节的答案。我个人不同意实施细节是有意义的。就在今天,我了解到,过去人们一直依靠Oracle在Oracle 8i中按操作执行按组排序(我相信),突然之间,一个较新的版本引入了散列group by,这打破了以下假设:可以依靠订购。换句话说:我喜欢用粗体字表示。毫无意义,而不是哦,如果您知道xyz版本的复杂细节,您实际上可以...
Lukas Eder

我已经添加了答案。我们是选择忽略非标准行为,还是在问题旁边有其他好的建议:给定查询的订单是否得到保证?是给Postgres的。它不适用于(或什至不适用)其他RDBMS。这适用于Postgres的所有现有版本,而不仅限于xyz版本。甚至有文档记录(带有保留)。您的报价有误导性。如果我们想忽略非标准行为,我们可以从Oracle开始,让我们相信NULL和空字符串是相同的。也正交于这个问题。
Erwin Brandstetter

@ErwinBrandstetter:有趣,感谢您的更新。是否保证您所指的是有据可查的?
卢卡斯·埃德

12

是。如果没有ORDER BY子句,则输出顺序是不确定的,并且查询计划程序处于其权限范围之内,以假定您知道并理解了这一点。

它可能会决定,由于外部查询未指定顺序,因此可以取消内部查询中的顺序以避免排序操作,特别是在没有聚簇索引或根本没有索引支持排序的情况下。如果不是现在,可能在未来的版本中做。

永远不要依赖不确定的行为。如果需要特定的顺序,请ORDER BY在适当的位置提供一个子句。


在PostgreSQL上测试时,排序是在顺序扫描后完成的,因为我在ORDER BY使用的列上没有任何索引。您认为哪个RDBMS会跳过内部查询ORDER BY?
Vlad Mihalcea

5
我不能说我知道任何什么,只是他们的任何和所有是完全可以这样做,如果他们希望的话-这将是根据一般标准和产品规格都完全可以接受的优化。SQL Server将完全拒绝该查询(除非您包括该查询,否则TOP 100%当前查询不能移植,这应该作为您项目的优先级。因为Postgres现在遵守内部查询的顺序并不意味着它将来总是会做(或旧版本做的,其实),所以你应该避免依赖的行为,以防万一。
大卫Spillett

1
@VladMihalcea,做“优化掉”冗余一个DBMS ORDER BYMariaDB的:为什么ORDER BY在FROM子查询忽略?
ypercubeᵀᴹ

6

它具有不确定行为的问题-对您有用,对我有用,在产品中重新格式化硬盘;)

我们可以退后一步说,从某种意义上说您是对的-没有理智的理由为什么任何理智的RDBMS都会重新排列内部选择中的行。但这并不能保证-意味着将来可能会有原因,供应商可以自由这样做。这意味着任何依赖于此行为的代码都将受供应商做出的更改的摆布,而他们没有义务公开,因为这不是API POV的重大更改。


2
它可以优化订单的一个原因是速度。以不同顺序返回行可能更有效。
TomTom

2
特别是,服务器可以利用并行性来读取表。如果这样做,则无需强制执行命令,您将获得这些行,但是线程会读取它们。(SQL Server实际上是这样做的,因此a SELECTno ORDER BY确实不是确定性的,而不仅仅是理论上的或因为数据已更改。)
Jeroen Mostert

@JeroenMostert:未定义的行为只会变得更糟。如果顺序混乱并且增量用于索引数组,会发生什么?
约书亚

2

是否真的有可能无法保证针对此特定的冗余派生表的顺序?

当前所有现有的Postgres(您正在测试)的版本的答案是: -针对此特定查询。排序顺序得到保证。

SQL Server人员对此会感到不舒服,因为Microsoft甚至不允许ORDER BY子查询。尽管如此,在Postgres中仍然可以保证此简单查询的排序顺序。ORDER BY在子查询中应用,并且外部查询不执行任何可能更改顺序的操作。

该手册甚至在“ 聚合函数 ”一章中也暗示了很多:

另外,通常也可以提供来自已排序子查询的输入值。

请注意,只有在外部查询级别不添加可能更改顺序的操作时,这才是正确的。因此,仅在简单情况下“保证”,并且不受SQL标准的支持。如果可以进行其他操作,Postgres可以免费重新订购。如有疑问ORDER BY,请在外部添加另一个SELECT。(在这种情况下,内部ORDER BY对于此简单查询将是多余的噪音。)


"table"不是简单的基本表而是复杂的视图或分区表时,是真的吗?当计划也有并行执行时,这是真的吗?在Postgres 10中也是如此吗?(我只是问,我不确定这些问题的答案是什么。)
ypercubeᵀᴹ17年

@ypercubeᵀᴹ:我还没有针对所有这些对Postgres 10进行测试,但是我很确定在任何情况下都是如此。对于简单情况,顺序在外部查询中被应用且未更改。
Erwin Brandstetter
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.