为什么此索引不会提高查询效果

时间:2017-06-16 20:22:37

标签: sql sql-server sql-server-2012 database-performance sql-tuning

平台:SQL Server 2012

背景:我有两个相当大的日志表 - 每个使用Pk / Fk连接的记录约为600k。为了参数,我们称它们为ReallyBigLog1和ReallyBigLog2。查询(下面)大约需要3.5秒才能运行。 WHERE子句包含三个不同的值。当被要求帮助改进此查询时,我立即注意到WHERE子句中的项目未编入索引。我沾沾自喜地建议添加索引 - 假设性能提升会让我看起来像个英雄。但是,附加指数没有可衡量的影响。

问题:鉴于下面的查询,为什么索引StartTime,EndTime和DateStamp对查询时间没有可测量的影响?

查询

SELECT 

    IrreleventField1,
    IrreleventField2,
    IrreleventField3....

    FROM  [dbo].[ReallyBigLog1] AS [T1]

    INNER JOIN [dbo].[ReallyBigLog2] AS [T2] ON [T1].[Id] = [T2].[Id]

    WHERE ([T1].[EndTime] IS NOT NULL) AND ([T1].[StartTime] IS NOT NULL) AND ([T2].[DateStamp] >= '2017-5-16 00:00:00')

索引

CREATE NONCLUSTERED INDEX [ix_RecommendedIndex]
ON [dbo].[ReallyBigLog1]
([StartTime] , [EndTime])

CREATE NONCLUSTERED INDEX [IX_DateStamp]
ON [dbo].[ReallyBigLog2]
([DateStamp])

执行计划

5 SELECT            
    4 Compute Scalar        
        3 Merge Join  / Inner Join Merge:([dbo].[ReallyBigLog1].[Id] [T2]=[dbo].[ReallyBigLog1].[Id] [T1]), Residual:([dbo].[ReallyBigLog2].[Id] as [T2].[Id]=[dbo].[ReallyBigLog1].[Id] as [T1].[Id])  
            1 Clustered Index Scan Predicate:([dbo].[ReallyBigLog1].[StartTime] as [T1].[StartTime] IS NOT NULL AND [dbo].[ReallyBigLog1].[EndTime] as [T1].[EndTime] IS NOT NULL), ORDERED FORWARD [dbo].[ReallyBigLog1].[PK_dbo.ReallyBigLog1] [T1]
            2 Clustered Index Scan Predicate:([dbo].[ReallyBigLog2].[DateStamp] as [T2].[DateStamp]>='2017-05-16 00:00:00.000'), ORDERED FORWARD [dbo].[ReallyBigLog2].[PK_dbo.ReallyBigLog2] [T2]

编辑(表格组成)

SELECT
  (SELECT COUNT(*) FROM ReallyBigLog1 WHERE StartTime IS NULL) as NullStartTime,
  (SELECT COUNT(*) FROM ReallyBigLog1 WHERE EndTime IS NULL) as NullEndTime,
  (SELECT COUNT(*) FROM ReallyBigLog1) as Log1Count,
  (SELECT COUNT(*) FROM ReallyBigLog2 WHERE DateStamp > '2017-5-16 00:00:00') AS DateStampUsage,
  (SELECT COUNT(*) FROM ReallyBigLog2) AS Log2Count

DateStampUsage  Log2Count   NullStartTime   NullEndTime  Log1Count
443038          651929      33748           34144        509545

3 个答案:

答案 0 :(得分:2)

除非你有很多空值,否则

ix_RecommendedIndex的帮助很小。

这里,真正重要的索引是IdsIX_DateStamp。由于您似乎在WHERE子句中有许多匹配数据,因此优化器更喜欢聚簇表扫描(合并Ids)。

使其更快的一种可能性是IX_DateStamp上的CLUSTERED索引,但它会对其他查询产生性能副作用,应首先在测试环境中强调。

如果您可以为EXPLAIN提供统计信息,则可能有助于更好的诊断。

编辑:根据提供的统计信息,我看不出如何使用索引来加快速度。有太多的数据需要解析(超过两个表的一半)。您可能需要整合数据设备,在另一个表中,或者在二进制级别优化数据(较小的记录大小以便更快地扫描)。

答案 1 :(得分:1)

由于您正在获取表中的大多数行,因此索引必须覆盖(=包含您在该表中的查询中所需的每一列)以帮助您 - 而且这种改进可能不会很多。

索引没有真正帮助的原因是您正在阅读大部分行,并且您的查询中有IrreleventField个。由于索引仅包含索引键+聚簇键,因此必须使用聚簇索引键从表(=聚簇索引)中提取其余字段。这称为密钥查找,并且成本非常高,因为必须对从索引中找到的符合搜索条件的每一行执行此操作。

对于所涵盖的索引,您可以添加"无关的"字段包括索引的一部分,如果你想尝试,如果它改善了情况。

答案 2 :(得分:1)

仅仅拥有日期和时间的索引并没有多大帮助。您应该有一个索引来覆盖连接的条件。例如ID列。由于您的查询主要是对T2别名的时间戳进行量化,因此我将提供以下索引

table           index
ReallyBigLog2   (DateStamp, ID )
ReallyBigLog1   (id, endTime, StartTime )

这就是原因。您正在寻找T2>中的交易。给定的日期。所以真正的大日志2 STARTS以此为基础。然后还包括JOIN基础的“ID”列到日志表1.此处的索引的两个部分都被覆盖,不需要进入数据页面进行比较以获取字段。

现在,T1的列索引。从作为立即找到的ID开始,或者不从T2表开始。将endTime,StartTime作为索引的一部分,再次,它不必转到原始数据页来限定WHERE / JOIN标准。

完成所有操作后,它会有一组记录,转到这些记录的数据页面,并提取您需要的其他详细信息。

from
   [dbo].[ReallyBigLog2] AS [T2]
      JOIN [dbo].[ReallyBigLog1] AS [T1]
         ON [T1].[Id] = [T2].[Id]
         AND ([T1].[EndTime] IS NOT NULL) 
         AND ([T1].[StartTime] IS NOT NULL) 
where
   [T2].[DateStamp] >= '2017-5-16 00:00:00'