为啥这个查询运行这么慢?

Posted

技术标签:

【中文标题】为啥这个查询运行这么慢?【英文标题】: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 秒! 你能发布创建表的脚本吗?连同索引定义 能否请您在某处发布实际执行计划,以便我们访问它并查看实际矩阵? 【参考方案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 上创建索引:

    公司ID 时间

您可能需要也可能不需要将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】:

想到两件事:

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

【讨论】:

【参考方案6】:

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

现在的执行计划:

感谢大家的努力。

【讨论】:

以上是关于为啥这个查询运行这么慢?的主要内容,如果未能解决你的问题,请参考以下文章

为啥这个 pg 查询这么慢?我怎样才能让它更快?

为啥这个查询这么慢? - PostgreSQL - 从 SERIAL、TIMESTAMP 和 NUMERIC(6,2) 中选择

为啥一个 group by 的聚合这么慢?

为啥我的 MongoDB 聚合查询这么慢

为啥我的解决方案这么慢,如何提高查询的性能?

为啥这个 jQuery 选择器这么慢?