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