SQL能否使用ORDER BY为同一查询的两次运行返回不同的结果?

SQL能否使用ORDER BY为同一查询的两次运行返回不同的结果?,sql,sql-server,sql-order-by,Sql,Sql Server,Sql Order By,我有下表: CREATE TABLE dbo.TestSort ( Id int NOT NULL IDENTITY (1, 1), Value int NOT NULL ) 值列可能(并且应该)包含重复项。 我们还假设表中已经有1000行 我试图证明关于不稳定排序的一点 给定此查询返回前1000个插入结果中10个结果的“页面”: SELECT TOP 10 * FROM TestSort WHERE Id <= 1000 ORDER BY Value 问题是:我的直觉正确吗?如果是

我有下表:

CREATE TABLE dbo.TestSort
(
Id int NOT NULL IDENTITY (1, 1),
Value int NOT NULL
) 
列可能(并且应该)包含重复项。
我们还假设表中已经有1000行

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

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

SELECT TOP 10 * FROM TestSort WHERE Id <= 1000 ORDER BY Value
问题是:我的直觉正确吗?如果是,您能否提供一个实际的操作示例,以产生不同的结果(至少“在您的机器上”)?您可以修改查询,在
列上添加索引等
我不关心确切的问题,但关心原则

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


如果不是,那为什么呢?

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

这类人解释了这一点:

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

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

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


尽管这特别指的是偏移量,但它显然适用于在没有这些子句的情况下多次运行查询。

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


在单线程环境中,每次的结果都是一样的。由于使用多线程,您无法保证。

如果您在订购时有领带,则订单不稳定

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
关键是我强制查询优化器使用并行计划,所以不能保证它会像不涉及并行性时聚集索引可能会做的那样按顺序读取数据

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


要了解更多信息,请阅读

事实上,我的实际查询启动了这个问题,它确实使用了Offset/Fetch(由Linq to EF生成)演示,检查结果1和2。要查看相同顶部(n)中的结果,您应该在运行前清除缓存。无法在提供的环境中执行此操作,因此我使用了两个查询,它们非常有趣。但是为什么呢?:)@LAD205在本地进行了尝试,并清除了缓存。给人印象深刻的如果您考虑将此扩展为一个答案,我会考虑授予接受的答案。非唯一列排序时的结果是不确定的,即使是单线程的。返回行的顺序可能取决于文件中页面的物理位置,该位置可能会因数据库维护而更改。不要认为数据库维护会在此处生效。我想我们讨论的是同样的结果。这意味着自上次运行语句以来没有任何变化。如果顺序不同,则受
TOP
OFFSET/FETCH
限制,结果也会不同。根据执行计划和页面的物理存储等因素,结果可能因偶然情况而有所不同。
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);