对于这个特定的冗余派生表,真的有可能无法保证顺序吗?

Vla*_*cea 13 mysql postgresql oracle order-by derived-tables

我在与 Lukas EderTwitter 对话中偶然发现了这个问题。

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

SELECT * 
FROM (
    SELECT * FROM table ORDER BY time DESC
) AS t
Run Code Online (Sandbox Code Playgroud)

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

因此,我假设 Planner 将简单地丢弃最外层的查询,因为它是多余的,或者只是通过内部表的结果。

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

Luk*_*der 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
Run Code Online (Sandbox Code Playgroud)

在这种特殊情况下,派生表的排序可以在最外层的 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
Run Code Online (Sandbox Code Playgroud)

可以合理地预期,至少在ROWNUM查询中,未来的 Oracle 版本不会破坏这种行为,以免破坏几乎所有遗留的 Oracle SQL,这些 SQL 尚未迁移到更理想的可读的 SQL 标准OFFSET .. FETCH语法:

SELECT * FROM table ORDER BY time DESC OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY
Run Code Online (Sandbox Code Playgroud)


Dav*_*ett 12

是的。如果没有ORDER BY子句,输出顺序是未定义的,查询计划器完全在其权限范围内,假设您知道并理解这一点。

它可能会决定,因为外部查询没有指定顺序,所以它可以删除内部查询中的排序以避免排序操作,尤其是在没有聚集索引或根本没有索引来支持排序的情况下。如果不是现在,可能在未来的版本中做。

永远不要依赖未定义的行为。如果您需要特定的排序,请ORDER BY在适当的位置给出一个子句。

  • 我不能说我知道什么*会*,只是如果他们愿意,他们可以完全自由地这样做 - 根据一般标准和产品规格,这将是一个完全可以接受的优化。SQL Server 将直接拒绝该查询(除非您包含“TOP 100%”,因此当前查询不可移植,这应该是您项目的优先事项。因为 Postgres 现在遵守内部查询中的顺序并不意味着它总是会将来做(或旧版本确实这样做),因此您应该避免依赖该行为以防万一。 (5认同)

小智 6

这是未定义行为的问题 - 对你有用,对我有用,在 prod 中重新格式化 HDD ;)

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

  • 它可以优化顺序的一个原因是速度。以不同的顺序返回行可能更有效。 (2认同)
  • 特别是,服务器可以利用并行性来读取表。如果这样做,并且不需要强制执行命令,您将取回行,但是线程读取它们。(SQL Server 实际上是这样做的,因此没有“ORDER BY”的“SELECT”确实是不确定的,不仅仅是理论上的,也不仅仅是因为数据发生了变化。) (2认同)