sha*_*oth 6 sql-server execution-plan azure-sql-database sorting
我有以下定义:
CREATE TABLE [dbo].[JobItems] (
[ItemId] UNIQUEIDENTIFIER NOT NULL,
[ItemState] INT NOT NULL,
[ItemCreationTime] DATETIME NULL DEFAULT GETUTCDATE(),
[ItemPriority] TINYINT NOT NULL DEFAULT(0),
[ItemRefreshTime] DATETIME NULL,
-- lots of other columns
CONSTRAINT [PrimaryKey_GUID_HERE] PRIMARY KEY NONCLUSTERED ([ItemId] ASC)
);
CREATE UNIQUE CLUSTERED INDEX [JobItemsIndex]
ON [dbo].[JobItems]([ItemId] ASC);
CREATE INDEX [GetTaskToProcessIndex]
ON [dbo].[JobItems]([ItemState], [ItemPriority], [ItemCreationTime])
Run Code Online (Sandbox Code Playgroud)
以及以下查询:
SELECT TOP(1) ItemId FROM JobItems
WHERE ItemState = 5 OR
( ( ItemState = 11 ) AND ( DATEDIFF( SECOND, ItemRefreshTime, GETUTCDATE() ) > 14 ) )
ORDER BY ItemPriority ASC, ItemCreationTime ASC
Run Code Online (Sandbox Code Playgroud)
我运行这个查询并检查实际的执行计划,这是发生了什么:
ItemState=5。ItemState=11然后对每一行进行单独的搜索以进行检索,ItemRefreshTime并使用嵌套循环过滤两次搜索的结果。ItemId,ItemCreationTime并ItemPriority连接起来,然后...DistinctSort发生与ORDER BY ItemId ASC最后TopNSort 发生在 ORDER BY ItemPriority ASC, ItemCreationTime ASCTopNSort并分别DistinctSort取 32% 这样的东西,所以我很乐意摆脱DistinctSort——我什至不明白它的目的。
这是什么神奇的TopNSort有用的东西DistinctSort,为什么它在那里?
我可以通过在您的问题中运行 DDL 然后摆弄统计信息来重现您在 SQL Server 2012 (on prem) 上描述的计划,以便 SQL Server 认为该表比实际情况大得多。
UPDATE STATISTICS [dbo].[JobItems] WITH ROWCOUNT = 10000000, pagecount = 10000000
Run Code Online (Sandbox Code Playgroud)
然后使用OPTION (MAXDOP 1, CONCAT UNION, ORDER GROUP).
这是一个索引联合计划。连接运算符实现UNION ALL. Distinct Sort 将语义更改为UNION操作以防止多次返回同一行。(如果表没有索引键作为唯一的行标识符,则此处将使用物理删除以避免错误地重复删除恰好具有相同列值的不同行)
在下面的查询中可能需要这样做的示例。(注意这两个参数被设置为相同的值,所以索引联合计划会寻找相同的行两次)
DECLARE @ItemState1 INT = 5
, @ItemState2 INT = 5
SELECT ItemId
FROM JobItems
WHERE ItemState = @ItemState1
OR ( ( ItemState = @ItemState2 )
AND ( DATEDIFF(SECOND, ItemRefreshTime, GETUTCDATE()) > 14 ) )
Run Code Online (Sandbox Code Playgroud)
Top N Sort 然后重新排序数据以实现TOP 1.
在您的情况下,出于多种原因,逻辑上不需要 Distinct Sort。在ItemState = 5和ItemState = 11分支相互排斥(这可以在编译时确定),并且另外TOP 1 ... ORDER BY ItemPriority ASC, ItemCreationTime ASC即使有错误的重复语义将不会受到影响。
编写查询的另一种方法(使用索引提供更好的计划以避免任何排序)是
SELECT TOP(1) ItemId
FROM (SELECT ItemId,
ItemPriority,
ItemCreationTime
FROM JobItems
WHERE ItemState = 5
UNION ALL
SELECT ItemId,
ItemPriority,
ItemCreationTime
FROM JobItems
WHERE ( ( ItemState = 11 )
AND ( DATEDIFF(SECOND, ItemRefreshTime, GETUTCDATE()) > 14 ) )) T
ORDER BY ItemPriority ASC,
ItemCreationTime ASC
Run Code Online (Sandbox Code Playgroud)
ItemRefreshTime如果实际上在找到满足残差谓词的单行之前可能需要相当多的列,您可以考虑将其作为包含列添加到索引中以避免键查找。