2

平台:SQL Server 2012的为什么这个指标并不能提高查询性能

背景:我有两个相当大的日志表 - 600K左右记录每个正在使用PK/FK加入。为了争论起见,我们称它们为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 
+1

优化器最有可能估计扫描表更便宜,特别是因为合并连接需要数据进行排序。多少数据与'DateStamp> ='2017-5-16''这个标准相符?这些时间中有多少时间为零? –

+0

谢谢James,我将该信息添加为该问题的编辑。 – SteveJ

+0

看着你的执行计划,它看起来像SQL服务器,根本没有使用新的索引,因此它没有改进任何..因为你在这里提供的有限的数据,我们没有什么可以明确地指定,说这篇文章可能会帮助你一下https://www.simple-talk.com/sql/performance/identifying-and-solving-index-scan-problems/。 – Surendra

回答

1

由于您正在提取表中的大部分行,因此索引必须覆盖(=包含您在查询中需要的每个列)以帮助您 - 而且这种改进可能不是许多。

索引没有真正帮助的原因是您正在阅读大部分行,并且在查询中有IrreleventField s。由于索引只包含索引键+聚簇键,所以必须使用聚簇索引键从表(=聚簇索引)中提取其余字段。这就是所谓的关键查找,并且可能会非常昂贵,因为必须为索引中找到的每一行找到符合您的搜索条件的行。

对于被覆盖的索引,您可以将“无关”字段添加到索引的包含部分,如果您想尝试是否改善了情况。

+0

詹姆斯,这很有道理 - 谢谢。为了确定我正确理解你,你所说的是即使我有一个索引 - 因为我的结果集几乎是整个表,而且我的字段不是索引的一部分 - 我必须将几乎整个表无论如何要获得这些额外的领域?但好奇的是,优化器如何知道提前知道并决定不在执行计划中使用索引? – SteveJ

+0

詹姆斯,你不会相信这一点(至少我没有),我改变了我的查询,只选择DateStamp,StartTime和EndTime。我的查询时间跳了5.4秒。现在比较慢。这似乎不可能。 – SteveJ

+0

添加到James所说的内容中,您可以尝试在StartTime上对ReallyBigLog1进行索引,然后将ReallyBigLog1中要从该表引用的所有其他列添加为包含列。然后运行你原来的查询。另外,确保您在外部ID上的ReallyBigLog2上有一个索引。 – hatchet

2

ix_RecommendedIndex将帮助非常差,除非你有很多空的。

这里,真正重要的指标是IdsIX_DateStamp。由于您似乎在WHERE子句中有大量匹配数据,因此优化程序更喜欢使用聚簇表扫描(合并Ids)。

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

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

编辑:随着提供的统计数据,我没有看到你如何使它更快,只是与索引。有太多的数据需要解析(超过一半的表)。您正在达到可能需要将数据应用程序整合到另一个表中的点,或者以二进制级别优化数据(更快的扫描更小的记录大小)。

+0

谢谢您花时间帮忙,但是,我不明白这个说法; “你可能需要存储专柜appart”。 – SteveJ

+0

对不起,这不是最好的词,我的意思是你可能需要将你的数据整合到另一个表中,无论你想在这里实现什么算法。 –

1

只有日期和时间的索引不会有太大的帮助。你应该有一个包含你的连接条件的索引,比如ID列。由于您的查询在T2别名的时间戳主要量化,我会提供以下指标

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

这里是为什么。您正在特定日期查找T2中的交易。所以真正的大日志2以此为基础。然后还包括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' 
+0

感谢您的帮助 - 那么,我是否需要将ID添加到复合索引中,因为它已经是PK了? – SteveJ

+1

@SteveJ,YES。即使是一个PK,一次只有一个索引用于查询where/join条件。通过这种方式,您的ID和日期部分正在考虑覆盖索引部分。 – DRapp