Sql-Server

需要幫助來了解調整慢速 SQL 伺服器查詢

  • July 2, 2020

對於我們的一個數據庫,如下所示的查詢非常慢。

由於安全原因,我無法分享實際的查詢或計劃,而只是想知道如何編寫查詢如下

SELECT [Id]
     ,[AboutMe]
     ,[Age]
     ,[CreationDate]
     ,[DisplayName]
     ,[DownVotes]
     ,[EmailHash]
     ,[LastAccessDate]
     ,[Location]
     ,[Reputation]
     ,[UpVotes]
     ,[Views]
     ,[WebsiteUrl]
     ,[AccountId]
 FROM [StackOverflow2010].[dbo].[Users]
 WHERE DisplayName IN (

SELECT DisplayName from dbo.Users
WHERE CAST(LastAccessDate AS DATE) = CAST ('20160814' AS DATE)
AND CreationDate>= DATEADD (DAY, -30,LastAccessDate)
AND CreationDate<= LastAccessDate)

在 stackoverflow 數據庫中,這不會返回任何行,但對於我們現有的 6 TBS 數據庫,它確實很慢。

CreationDateLastAccessdate列都是日期時間(10)

並且DisplayName是 Varchar(50)

如果上面可以重寫,請建議我如何提高性能

據我所知,您可以通過添加索引來大大改進事情而無需重寫查詢。

這是我在 SQL Server 2019 上獲得的執行計劃(請注意,我將日期更改為'20090814'只是為了返回一些結果):

在此處輸入圖像描述

Table 'Users'. Scan count 18, logical reads 15430

SQL Server Execution Times:
  CPU time = 297 ms,  elapsed time = 94 ms.

這將掃描整個使用者表一次以獲取滿足日期條件的使用者,然後再掃描幾次以獲取具有匹配顯示名稱的其餘使用者。

不理想。

在我的機器上查詢只需要 94 毫秒,主要是因為一切都在 RAM 中,並且查詢以並行度 (DOP) 8 執行。

但是,添加這兩個索引對這種情況有很大幫助:

CREATE NONCLUSTERED INDEX IX_LastAccessDate 
ON dbo.Users (LastAccessDate)
INCLUDE (CreationDate, DisplayName);

CREATE NONCLUSTERED INDEX IX_DisplayName
ON dbo.Users (DisplayName);

現在我得到了這個執行計劃:

計劃資源管理器中執行計劃的螢幕截圖,顯示索引搜尋而不是掃描

Table 'Users'. Scan count 15, logical reads 153

SQL Server Execution Times:
  CPU time = 0 ms,  elapsed time = 0 ms.

SQL Server 能夠在此處生成一個查找計劃,方法是使用內部GetRangeThroughConvert函式來確定datetime等效於 的可能值的範圍CAST(LastAccessDate AS DATE) = CAST ('20160814' AS DATE)。本質上,它會在後台重寫查詢以匹配Mo64 提出的建議

注意:您最好重寫以顯式使用該範圍查詢,而不是依賴於隱藏的隱式轉換

您可以在 Paul White 的部落格上閱讀有關此類執行計劃的更多詳細資訊:Dynamic Seeks and Hidden Implicit Conversions

如果您消除鍵查找,則可以進一步改善這種情況 - 通過選擇更少的列,或者在第一個索引中包括所有必要的列(on LastAccessDate)。


我意識到這是一個玩具範例,但希望它說明了可以應用於您的實際情況的通用解決方案:

  • 添加一個索引,允許 SQL Server 在子查詢中跳轉到正確的日期
  • CAST可能將from datetimeto重寫date為兩個不等式條件。換句話說,替換了這個:
WHERE CAST(LastAccessDate AS DATE) = CAST ('20160814' AS DATE)

有了這個:

WHERE LastAccessDate >= '20160814' AND LastAccessDate < '20160815' 
  • 添加一個索引,允許 SQL Server 跳轉到匹配的varchar(50)列值

無論如何,您將始終進行全表/索引掃描(無搜尋)。

SQL 無法“猜測”“CreationDate<= LastAccessDate”,因此它必須讀取每一行來比較兩個日期。

如果您的桌子很大,則需要時間。

您可以嘗試在“CreationDate<= LastAccessDate”上使用計算列,這可以為 SQL 提供一些統計資訊。

如果您在其上放置一個索引,那麼您可能會看到巨大的改進。

引用自:https://dba.stackexchange.com/questions/270227