需要幫助來了解調整慢速 SQL 伺服器查詢
對於我們的一個數據庫,如下所示的查詢非常慢。
由於安全原因,我無法分享實際的查詢或計劃,而只是想知道如何編寫查詢如下
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 數據庫,它確實很慢。
CreationDate
和LastAccessdate
列都是日期時間(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
可能將fromdatetime
to重寫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 提供一些統計資訊。
如果您在其上放置一個索引,那麼您可能會看到巨大的改進。