平台: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
答案 0 :(得分:2)
ix_RecommendedIndex
的帮助很小。
这里,真正重要的索引是Ids
和IX_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'