为什么在我的查询计划中 DistinctSort 后跟 TopNSort?

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)

我运行这个查询并检查实际的执行计划,这是发生了什么:

  1. 完成索引查找以检索带有 的项目ItemState=5
  2. 完成索引搜索以检索项目,ItemState=11然后对每一行进行单独的搜索以进行检索,ItemRefreshTime并使用嵌套循环过滤两次搜索的结果。
  3. 来自 1 和 2 的集合包含ItemId,ItemCreationTimeItemPriority连接起来,然后...
  4. 神奇的DistinctSort发生与ORDER BY ItemId ASC最后
  5. TopNSort 发生在 ORDER BY ItemPriority ASC, ItemCreationTime ASC

TopNSort并分别DistinctSort取 32% 这样的东西,所以我很乐意摆脱DistinctSort——我什至不明白它的目的。

这是什么神奇的TopNSort有用的东西DistinctSort,为什么它在那里?

Mar*_*ith 7

我可以通过在您的问题中运行 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 = 5ItemState = 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如果实际上在找到满足残差谓词的单行之前可能需要相当多的列,您可以考虑将其作为包含列添加到索引中以避免键查找。