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

前端之家收集整理的这篇文章主要介绍了sql – 避免索引计划中的排序运算符前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有两个表[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

我想从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

运行此查询后,这是我的执行计划:

正如您所看到的那样,有一个排序运算符即将发挥作用,这可能是因为以下行“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

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

SELECT  DI.LogID              
FROM    LogTable DI              
  --      INNER JOIN LogTable_Cross DP ON DP.LogID = DI.LogID  
        --WHERE  DP.UserID = 1  
ORDER BY DateSent DESC

计划变为

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

因此,即使我正在使用join,也可以在计划中删除“排序”运算符.

编辑:

我走得更远,将“最大平行度”限制为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

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

编辑2

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

CREATE NONCLUSTERED INDEX [IX_LogTable_Cross_UserID_LogID_2]
 ON [dbo].[LogTable_Cross] ([UserID],[LogID])

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

解决方法

您的第二个查询不包含UserId条件,因此它不是等效查询. LogTable上的索引未涵盖第一个查询的原因是UserId不存在于其中(并且您还需要执行连接).因此,sql Server必须连接表(散列连接,合并连接或嵌套循环连接). sql Server正确选择了哈希联接,因为中间结果很大,并且它们没有根据LogID进行排序.如果你给他们根据LogID(你的第二个编辑)排序的中间结果然后他使用合并连接,但是,根据DateSend排序需要stil.没有sort的唯一解决方案是创建索引的物化视图:
CREATE VIEW vLogTable
WITH SCHEMABINDING
AS
   SELECT  DI.LogID,DI.DateSent,DP.UserID           
   FROM dbo.LogTable DI              
   INNER JOIN dbo.LogTable_Cross DP ON DP.LogID = DI.LogID  

CREATE UNIQUE CLUSTERED INDEX CIX_vCustomerOrders 
   ON dbo.vLogTable(UserID,DateSent,LogID);

该视图必须与noexpand提示一起使用,因此优化器可以找到CIX_vCustomerOrders索引:

SELECT  LogID              
FROM dbo.vLogTable   WITH(NOEXPAND)
    WHERE  UserID = 1  
ORDER BY DateSent DESC

查询与您的第一个查询等效查询.如果插入以下行,可以检查正确性:

INSERT INTO LogTable VALUES (CURRENT_TIMESTAMP)

然后我的查询仍然返回正确的结果(10000行),您的第二个查询返回10001行.您可能会尝试删除或插入其他一些行,视图仍然是最新的,您可以从我的查询中收到正确的结果.

猜你在找的MsSQL相关文章