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行.您可能会尝试删除或插入其他一些行,视图仍然是最新的,您可以从我的查询中收到正确的结果. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |