为什么这个查询运行得这么慢?

时间:2015-04-15 03:57:28

标签: sql-server database query-optimization

此查询运行速度非常快(<100毫秒):

SELECT TOP (10) 
    [Extent2].[CompanyId] AS [CompanyId]
    ,[Extent1].[Id] AS [Id]
    ,[Extent1].[Status] AS [Status]
FROM [dbo].[SplittedSms] AS [Extent1]
INNER JOIN [dbo].[Sms] AS [Extent2]
    ON [Extent1].[SmsId] = [Extent2].[Id]
WHERE [Extent2].[CompanyId] = 4563 
    AND ([Extent1].[NotifiedToClient] IS NULL)

如果我只添加一个时间过滤器,则需要太长时间(22秒!):

SELECT TOP (10) 
    [Extent2].[CompanyId] AS [CompanyId]
    ,[Extent1].[Id] AS [Id]
    ,[Extent1].[Status] AS [Status]
FROM [dbo].[SplittedSms] AS [Extent1]
INNER JOIN [dbo].[Sms] AS [Extent2]
    ON [Extent1].[SmsId] = [Extent2].[Id]
WHERE [Extent2].Time > '2015-04-10'
    AND [Extent2].[CompanyId] = 4563 
    AND ([Extent1].[NotifiedToClient] IS NULL)

我尝试在[Time]表的Sms列上添加索引,但优化器似乎没有使用索引。尝试使用With (index (Ix_Sms_Time));但令我惊讶的是,它需要更多的时间(29秒!)。

这是实际的执行计划: enter image description here

两个查询的执行计划相同。这里提到的表具有5M到8M行(索引<1%碎片并且统计信息被更新)。我在16核32GB内存Windows 2008 R2机器上使用MS SQL Server 2008R2)

6 个答案:

答案 0 :(得分:0)

只有在客户端过滤器运行后强制时间过滤器启动时,它是否有帮助?

FI就像在这个例子中一样:

;WITH ClientData AS (   
    SELECT 
         [E2].[CompanyId]
        ,[E2].[Time]
        ,[E1].[Id]
        ,[E1].[Status]
    FROM [dbo].[SplittedSms] AS [E1]
    INNER JOIN [dbo].[Sms] AS [E2]
        ON [E1].[SmsId] = [E2].[Id]
    WHERE  [E2].[CompanyId] = 4563 
      AND ([E1].[NotifiedToClient] IS NULL)
)
SELECT TOP 10
     [CompanyId]    
    ,[Id]
    ,[Status]
FROM ClientData
WHERE [Time] > '2015-04-10'

答案 1 :(得分:0)

使用以下Sms(按此顺序)在Index Key Columns上创建索引:

  1. CompanyID
  2. 时间
  3. 您可能需要也可能不需要将Id添加为Included Column

答案 2 :(得分:0)

您的时间列是什么数据类型? 如果是日期时间,请尝试转换您的&#39; 2015-04-10&#39;进入等效的数据类型,以便它可以使用索引。

Declare @test datetime
Set @test='2015-04-10'

然后修改你的条件:

[Extent2].Time > @test    

如果存在数据类型不匹配,则sql server会隐式转换为匹配的数据类型。任何函数或强制转换操作都会阻止使用索引。

答案 3 :(得分:0)

我和@JonTirjan在同一条轨道上,只有时间的索引导致很多关键查找,所以你应该至少尝试一下:

create index xxx on Sms (Time, CompanyId) include (Id)

create index xxx on Sms (CompanyId, Time) include (Id)

如果Id是您的聚簇索引,则include子句中不需要它。如果您的数据的重要部分属于CompanyID 4563,那么也可以将其作为包含列。

您在实际计划中看到的百分比只是基于行数假设的估计值,因此有时完全错误。查看实际行数/执行次数+统计数据IO输出应该可以让您了解实际发生的情况。

答案 4 :(得分:0)

我想到了两件事:

  1. 通过添加一个额外的限制,它会变得更加困难。为数据库找到符合您限制的前10个项目。从let&#39; s中找出前10行表示10.000项(总计1百万)更容易找到100个项目中的前10行(总计1个百万)。
  2. 索引未被使用可能是因为索引是在datetime列上创建的,如果您还在其中存储时间,则效率不高。您可能希望在[time]列上创建聚簇索引(但是您必须删除现在在[CompanyId]列上的聚簇索引,或者您可以创建一个存储[的日期部分]的计算列。 time]列,在此计算列上创建索引并过滤此列。

答案 5 :(得分:0)

我发现SmsId表上的外键列(SplittedSms)上没有索引。我做了一个,似乎第二个查询几乎和现在的第一个一样快。

现在执行计划:

enter image description here

感谢大家的努力。