使用 ORDER BY 两次运行同一个查询可以得到 SQL return 不同的结果吗?
Can SQL return different results for two runs of the same query using ORDER BY?
我有以下 table:
CREATE TABLE dbo.TestSort
(
Id int NOT NULL IDENTITY (1, 1),
Value int NOT NULL
)
Value
列可能(并且应该)包含重复项。
我们还假设 table.
中已经有 1000 行
我想证明一个关于 unstable 排序的观点。
假设此查询 return 从前 1000 个插入的结果中 'page' 得到 10 个结果:
SELECT TOP 10 * FROM TestSort WHERE Id <= 1000 ORDER BY Value
我的直觉告诉我,如果 Value
列包含重复值,则此查询的两次运行可能 return 不同的行。
我的依据是:
- 排序不是stable
- 如果在两次查询运行之间的 table 中插入新行,它可能会创建 B 树的重新平衡(
Value
列可能被索引或不被索引)
编辑:为了完整性:我假设行一旦插入就永远不会改变,也永远不会被删除。
相比之下,使用 stable 排序的查询(也按 Id 排序)应该总是 return 相同的结果,因为 ID
是唯一的:
SELECT TOP 10 * FROM TestSort WHERE Id <= 1000 ORDER BY Value, Id
问题是:我的直觉是否正确?如果是,您能否提供一个会产生不同结果(至少 "on your machine")的实际操作示例?您可以修改查询,在 Values
列上添加索引等
我不关心具体查询,只关心原理
我正在使用 MS SQL 服务器 (2014),但我对任何 SQL 数据库的答案同样满意。
如果不是,那是为什么?
您的直觉是正确的。在 SQL 中,order by
的排序不稳定。因此,如果您有领带,它们可以按任何顺序退回。而且,顺序可以从一个 运行 变为另一个。
documentation 的解释如下:
Using OFFSET and FETCH as a paging solution requires running the query
one time for each "page" of data returned to the client application.
For example, to return the results of a query in 10-row increments,
you must execute the query one time to return rows 1 to 10 and then
run the query again to return rows 11 to 20 and so on. Each query is
independent and not related to each other in any way. This means that,
unlike using a cursor in which the query is executed once and state is
maintained on the server, the client application is responsible for
tracking state. To achieve stable results between query requests using
OFFSET and FETCH, the following conditions must be met:
The underlying data that is used by the query must not change. That is, either the rows touched by the query are not updated or all
requests for pages from the query are executed in a single transaction
using either snapshot or serializable transaction isolation. For more
information about these transaction isolation levels, see SET
TRANSACTION ISOLATION LEVEL (Transact-SQL).
The ORDER BY clause contains a column or combination of columns that are guaranteed to be unique.
虽然这具体指的是 offset
/fetch
,但它显然适用于 运行 没有这些子句的多次查询。
我想这个 post 会回答你的问题:
Is SQL order by clause guaranteed to be stable ( by Standards)
在单线程环境中,结果每次都是一样的。由于使用了多线程,所以不能保证。
如果您在订购时有领带 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
示例:
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
。
我有以下 table:
CREATE TABLE dbo.TestSort
(
Id int NOT NULL IDENTITY (1, 1),
Value int NOT NULL
)
Value
列可能(并且应该)包含重复项。
我们还假设 table.
我想证明一个关于 unstable 排序的观点。
假设此查询 return 从前 1000 个插入的结果中 'page' 得到 10 个结果:
SELECT TOP 10 * FROM TestSort WHERE Id <= 1000 ORDER BY Value
我的直觉告诉我,如果 Value
列包含重复值,则此查询的两次运行可能 return 不同的行。
我的依据是:
- 排序不是stable
- 如果在两次查询运行之间的 table 中插入新行,它可能会创建 B 树的重新平衡(
Value
列可能被索引或不被索引)
编辑:为了完整性:我假设行一旦插入就永远不会改变,也永远不会被删除。
相比之下,使用 stable 排序的查询(也按 Id 排序)应该总是 return 相同的结果,因为 ID
是唯一的:
SELECT TOP 10 * FROM TestSort WHERE Id <= 1000 ORDER BY Value, Id
问题是:我的直觉是否正确?如果是,您能否提供一个会产生不同结果(至少 "on your machine")的实际操作示例?您可以修改查询,在 Values
列上添加索引等
我不关心具体查询,只关心原理
我正在使用 MS SQL 服务器 (2014),但我对任何 SQL 数据库的答案同样满意。
如果不是,那是为什么?
您的直觉是正确的。在 SQL 中,order by
的排序不稳定。因此,如果您有领带,它们可以按任何顺序退回。而且,顺序可以从一个 运行 变为另一个。
documentation 的解释如下:
Using OFFSET and FETCH as a paging solution requires running the query one time for each "page" of data returned to the client application. For example, to return the results of a query in 10-row increments, you must execute the query one time to return rows 1 to 10 and then run the query again to return rows 11 to 20 and so on. Each query is independent and not related to each other in any way. This means that, unlike using a cursor in which the query is executed once and state is maintained on the server, the client application is responsible for tracking state. To achieve stable results between query requests using OFFSET and FETCH, the following conditions must be met:
The underlying data that is used by the query must not change. That is, either the rows touched by the query are not updated or all requests for pages from the query are executed in a single transaction using either snapshot or serializable transaction isolation. For more information about these transaction isolation levels, see SET TRANSACTION ISOLATION LEVEL (Transact-SQL).
The ORDER BY clause contains a column or combination of columns that are guaranteed to be unique.
虽然这具体指的是 offset
/fetch
,但它显然适用于 运行 没有这些子句的多次查询。
我想这个 post 会回答你的问题:
Is SQL order by clause guaranteed to be stable ( by Standards)
在单线程环境中,结果每次都是一样的。由于使用了多线程,所以不能保证。
如果您在订购时有领带 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
示例:
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
。