我有两个表[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])
解决方法
您的第二个查询不包含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行.您可能会尝试删除或插入其他一些行,视图仍然是最新的,您可以从我的查询中收到正确的结果.