避免索引计划中的排序运算符

Rag*_*hav 9 sql indexing sql-server-2008 database-tuning sql-server-2012

我有两个表[LogTable]和[LogTable_Cross].

下面是填充它们的架构和脚本:

 --Main Table

 CREATE TABLE [dbo].[LogTable]
    (
      [LogID] [int] NOT NULL
                    IDENTITY(1, 1) ,
      [DateSent] [datetime] NULL,
    )
 ON [PRIMARY]
GO
 ALTER TABLE [dbo].[LogTable] ADD CONSTRAINT [PK_LogTable] PRIMARY KEY CLUSTERED  ([LogID]) ON [PRIMARY]
GO
 CREATE NONCLUSTERED INDEX [IX_LogTable_DateSent] ON [dbo].[LogTable] ([DateSent] DESC) ON [PRIMARY]
GO
 CREATE NONCLUSTERED INDEX [IX_LogTable_DateSent_LogID] ON [dbo].[LogTable] ([DateSent] DESC) INCLUDE ([LogID]) ON [PRIMARY]
GO


--Cross table

 CREATE TABLE [dbo].[LogTable_Cross]
    (
      [LogID] [int] NOT NULL ,
      [UserID] [int] NOT NULL
    )
 ON [PRIMARY]
GO
 ALTER TABLE [dbo].[LogTable_Cross] WITH NOCHECK ADD CONSTRAINT [FK_LogTable_Cross_LogTable] FOREIGN KEY ([LogID]) REFERENCES [dbo].[LogTable] ([LogID])
GO
 CREATE NONCLUSTERED INDEX [IX_LogTable_Cross_UserID_LogID]
 ON [dbo].[LogTable_Cross] ([UserID])
 INCLUDE ([LogID])
GO


-- Script to populate them
 INSERT INTO [LogTable]
        SELECT TOP 100000
                DATEADD(day, ( ABS(CHECKSUM(NEWID())) % 65530 ), 0)
        FROM    sys.sysobjects
                CROSS JOIN sys.all_columns


 INSERT INTO [LogTable_Cross]
        SELECT  [LogID] ,
                1
        FROM    [LogTable]
        ORDER BY NEWID()

 INSERT INTO [LogTable_Cross]
        SELECT  [LogID] ,
                2
        FROM    [LogTable]
        ORDER BY NEWID()

 INSERT INTO [LogTable_Cross]
        SELECT  [LogID] ,
                3
        FROM    [LogTable]
        ORDER BY NEWID()


GO
Run Code Online (Sandbox Code Playgroud)

我想从datesent desc中选择已经给出userid(用户id将从交叉表LogTable_Cross检查)的所有日志(来自LogTable).

SELECT  DI.LogID              
FROM    LogTable DI              
        INNER JOIN LogTable_Cross DP ON DP.LogID = DI.LogID  
        WHERE  DP.UserID = 1  
ORDER BY DateSent DESC
Run Code Online (Sandbox Code Playgroud)

运行此查询后,这是我的执行计划: 在此输入图像描述

正如您所看到的那样,有一个排序运算符即将发挥作用,这可能是因为以下行"ORDER BY DateSent DESC"

我的问题是,即使我在表上应用了以下索引,为什么Sort运算符会进入计划

GO
 CREATE NONCLUSTERED INDEX [IX_LogTable_DateSent] ON [dbo].[LogTable] ([DateSent] DESC) ON [PRIMARY]
GO
 CREATE NONCLUSTERED INDEX [IX_LogTable_DateSent_LogID] ON [dbo].[LogTable] ([DateSent] DESC) INCLUDE ([LogID]) ON [PRIMARY]
GO
Run Code Online (Sandbox Code Playgroud)

另一方面,如果我删除连接并以这种方式写入查询:

SELECT  DI.LogID              
FROM    LogTable DI              
  --      INNER JOIN LogTable_Cross DP ON DP.LogID = DI.LogID  
        --WHERE  DP.UserID = 1  
ORDER BY DateSent DESC
Run Code Online (Sandbox Code Playgroud)

计划变为

在此输入图像描述

即删除排序运算符,计划显示我的查询正在使用我的非聚集索引.

那么即使我正在使用连接,这是一种在我的查询计划中删除"排序"运算符的方法.

编辑:

我进一步将"最大平行度"限制为1

在此输入图像描述

再次执行以下查询:

SELECT  DI.LogID              
FROM    LogTable DI              
        INNER JOIN LogTable_Cross DP ON DP.LogID = DI.LogID  
        WHERE  DP.UserID = 1  
ORDER BY DateSent DESC
Run Code Online (Sandbox Code Playgroud)

并且该计划仍然具有Sort运算符:

在此输入图像描述

编辑2

即使我有如下建议的索引:

 CREATE NONCLUSTERED INDEX [IX_LogTable_Cross_UserID_LogID_2]
 ON [dbo].[LogTable_Cross] ([UserID], [LogID])
Run Code Online (Sandbox Code Playgroud)

该计划仍然具有Sort运算符: 在此输入图像描述

Pre*_*red 1

由于前面步骤中的并行性,当您进行连接时,您将进行排序操作。当SQL Server在多个线程中处理记录时,顺序不再确定。每个线程只是将结果推送到管道中的下一个项目(在您的情况下是哈希匹配)。

由于顺序尚未确定并且您正在请求顺序,因此 SQL Server 必须对结果进行排序。

您可以尝试添加MAXDOP = 1提示以强制 SQL Server 仅使用一个线程运行查询。这在这种情况下可能会有所帮助,但也会导致性能下降。

第二个查询可以使用索引扫描来满足,并且索引是有序的,并且该顺序与请求的顺序相同。索引中的记录(键)按定义排序。SQL Server 猜测,在一个线程上运行查询并仅使用索引读取数据比使用多个线程读取数据并稍后对其进行排序更有利。