【问题标题】:Why this query is running so slow?为什么这个查询运行这么慢?
【发布时间】:2015-04-15 03:57:28
【问题描述】:

此查询运行速度非常快(

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)

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

这是实际的执行计划:

两个查询的执行计划相同。这里提到的表有 5M 到 8M 行(索引是

【问题讨论】:

  • 尝试添加 ORDER BY ASC 或 DESC TIME 列并检查。它应该运行得更快。
  • 首先,没有ORDER BYTOP 查询返回的行是不确定的。最佳实践是使用TOP 指定ORDER BY。要提高第二个查询的性能,请尝试对 CompanyID 和 Time 使用复合非聚集索引。这将避免接触行
  • 试过Order By [Time];更糟糕的是,29 秒!
  • 你能发布创建表的脚本吗?连同索引定义
  • 能否请您在某处发布实际执行计划,以便我们访问它并查看实际矩阵?

标签: sql-server database query-optimization


【解决方案1】:

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

像这个例子中的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'

【讨论】:

    【解决方案2】:

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

    1. 公司ID
    2. 时间

    您可能需要也可能不需要将Id 添加为Included Column

    【讨论】:

      【解决方案3】:

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

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

      然后修改你的条件:

      [Extent2].Time > @test    
      

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

      【讨论】:

      • 将日期更改为变量将导致优化器针对未知值优化子句(如果它不是存储过程),并且在日期时间字段和日期接近当天的情况下,它可能会使情况更糟。具有正确格式的日期时间,例如'20150410' 应该没问题。
      【解决方案4】:

      我与@JonTirjan 处于同一轨道,只有 Time 的索引会导致很多关键查找,因此您至少应该尝试以下操作:

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

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

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

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

      【讨论】:

        【解决方案5】:

        想到两件事:

        1. 通过添加额外的限制,数据库将“更难”找到符合您的限制的前 10 个项目。从假设 10.000 个项目(总共 100 万个)中查找前 10 行比从大约 100 个项目(总共 100 万个)中查找前 10 行更容易。
        2. 该索引未使用可能是因为该索引是在日期时间列上创建的,如果您还要在其中存储时间,则效率不高。您可能希望在 [time] 列上创建一个聚集索引(但是您必须删除现在位于 [CompanyId] 列上的聚集索引,或者您可以创建一个计算列来存储 [ time] 列,在此计算列上创建索引并在此列上过滤。

        【讨论】:

          【解决方案6】:

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

          现在的执行计划:

          感谢大家的努力。

          【讨论】:

            猜你喜欢
            • 2011-10-18
            • 2011-03-11
            • 2020-03-19
            • 2014-03-12
            • 2011-02-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多