【问题标题】:Can SQL return different results for two runs of the same query using ORDER BY?SQL 能否使用 ORDER BY 为同一查询的两次运行返回不同的结果?
【发布时间】:2015-10-15 12:16:50
【问题描述】:

我有下表:

CREATE TABLE dbo.TestSort
(
Id int NOT NULL IDENTITY (1, 1),
Value int NOT NULL
) 

Value 列可能(并且预计会)包含重复项。
我们还假设表中已经有 1000 行。

我试图证明关于不稳定排序的观点。

鉴于此查询从前 1000 个插入的结果中返回 10 个结果的“页面”:

SELECT TOP 10 * FROM TestSort WHERE Id <= 1000 ORDER BY Value

我的直觉告诉我,如果Value 列包含重复值,则此查询的两次运行可能会返回不同的行。

我基于以下事实:

  • 排序不稳定
  • 如果在两次查询运行之间在表中插入新行,则可能会创建 B 树的重新平衡(Value 列可能被索引或不被索引)

编辑:为了完整性:我假设行一旦插入就永远不会改变,也永远不会被删除。

相比之下,具有稳定排序(也按 Id 排序)的查询应该总是返回相同的结果,因为 IDs 是唯一的:

SELECT TOP 10 * FROM TestSort WHERE Id <= 1000 ORDER BY Value, Id

问题是:我的直觉正确吗?如果是,您能否提供一个会产生不同结果的实际操作示例(至少“在您的机器上”)?您可以修改查询,在 Values 列上添加索引等。
我不在乎确切的查询,而是在乎原理。

我正在使用 MS SQL Server (2014),但对任何 SQL 数据库的答案都同样满意。

如果不是,那为什么?

【问题讨论】:

  • Demo data.stackexchange.com/stackoverflow/query/375976 ,检查结果 1 和 2。要在相同的 TOP (n) 中查看结果,您应该在运行前清除缓存。无法在提供的环境中执行此操作,因此我使用了两个查询。
  • @lad2025 非常有趣。但为什么? :)
  • @lad2025 在本地尝试过,清除缓存。感人的!如果您考虑将此扩展到答案,我会考虑授予接受的答案。

标签: sql sql-server sql-order-by


【解决方案1】:

你的直觉是正确的。在 SQL 中,order by 的排序不稳定。因此,如果您有领带,可以按任何顺序退回。而且,顺序可以从一个运行更改为另一个运行。

documentation 对此进行了解释:

使用 OFFSET 和 FETCH 作为分页解决方案需要运行查询 返回给客户端应用程序的每一“页”数据一次。 例如,要以 10 行增量返回查询结果, 您必须执行一次查询以返回第 1 到 10 行,然后 再次运行查询以返回第 11 到 20 行,依此类推。每个查询都是 独立且不以任何方式相互关联。这意味着, 不像使用游标,其中查询执行一次,状态为 在服务器上维护,客户端应用程序负责 跟踪状态。为了在查询请求之间实现稳定的结果,使用 OFFSET 和 FETCH,必须满足以下条件:

  • 查询使用的基础数据不得更改。也就是说,查询所触及的行要么没有更新,要么全部 来自查询的页面请求在单个事务中执行 使用快照或可序列化事务隔离。更多 有关这些事务隔离级别的信息,请参阅 SET 事务隔离级别 (Transact-SQL)。

  • ORDER BY 子句包含保证唯一的列或列组合。

虽然这特指offset/fetch,但它显然适用于在没有这些子句的情况下多次运行查询。

【讨论】:

  • 实际上,我在现实生活中开始这个问题的查询确实使用了 Offset/Fetch(它是由 Linq to EF 生成的)
【解决方案2】:

如果您在订购时有联系,则订购方式不稳定。

LiveDemo

CREATE TABLE #TestSort
(
Id INT NOT NULL IDENTITY (1, 1) PRIMARY KEY,
Value INT NOT NULL
) ;

DECLARE @c INT = 0;

WHILE @c < 100000
BEGIN
  INSERT INTO #TestSort(Value)
  VALUES ('2');

  SET @c += 1;
END

例子:

SELECT TOP 10 * 
FROM #TestSort 
ORDER BY Value
OPTION (MAXDOP 4);

DBCC DROPCLEANBUFFERS;  -- run to clear cache

SELECT TOP 10 * 
FROM #TestSort 
ORDER BY Value
OPTION (MAXDOP 4);

关键是我强制查询优化器使用并行计划,因此不能保证它会像聚集索引在不涉及并行性时那样按顺序读取数据。

除非您使用ORDER BY Id, Value 明确强制以特定方式对结果进行排序,否则您无法确定查询优化器将如何读取数据。

更多信息请阅读No Seatbelt - Expecting Order without ORDER BY

【讨论】:

    【解决方案3】:

    我认为这篇文章会回答你的问题:

    Is SQL order by clause guaranteed to be stable ( by Standards)

    当您在单线程环境中时,结果每次都是一样的。由于使用了多线程,所以不能保证。

    【讨论】:

    • 按非唯一列排序时的结果是不确定的,即使是单线程的。返回的行的顺序可能取决于文件中页面的物理位置,这可能会因数据库维护而改变。
    • 不要以为数据库维护是什么都会在这里生效。我认为我们只是按另一个顺序讨论相同的结果。这意味着自上一条语句运行以来没有任何变化。
    • 如果顺序不同,受TOPOFFSET/FETCH限制,结果也不同。结果可能因偶然因素而异,具体取决于执行计划和页面物理存储等因素。
    猜你喜欢
    • 2015-05-05
    • 1970-01-01
    • 1970-01-01
    • 2017-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-25
    相关资源
    最近更新 更多