MS SQL Server 中的慢速計算排序
我有一個大查詢(實體框架生成一個),我正在使用計算欄位。通過調整聚集索引以包括
$$ On $$(日期時間)列。 根據查詢計劃,計算函式上的排序負責 100% 的性能緩慢。
該函式估計項目的順序並根據收到的投票/評論對其進行排名,並為較新的項目提供更高的排名。所以分數每小時變化一次 + 對項目/評論的每一次投票(類似於 Reddit)。
簡而言之,這就是它的排名
CAST( [Project4].[C1] AS float) / (POWER( CAST( DATEDIFF (hour, [Project4].[On], '2018-05-01 00:00:00.0000000') AS float) + cast(2 as float(53)), cast(1.8 as float(53)))) AS [C1],
問題是,當我按
$$ On $$,我有立竿見影的效果。當我按上述計算排序時,需要 12-16 秒。 文字
'2018-05-01 00:00:00.0000000'
取決於目前時間。我將如何改善這一點?
編輯:我認為發布查詢沒有用,因為它是由 EF 創建的並且非常大(對於 Stackoverflow,請參閱此pastebin)
更有用:粘貼計劃
您不應該在 SQL Server 中進行這種排序,而應該在您的應用程序中進行。原因如下:
當您需要對沒有支持所需排序順序的索引的數據進行排序時,您的查詢將在執行時使用排序運算符完成所有工作。
SELECT * FROM dbo.Users AS u WHERE u.Reputation < 2 ORDER BY u.Reputation;
Sort 運算符將請求記憶體以按您排序的列對您請求的所有列進行排序(這已大大簡化,但在這裡並不需要更深入)。
如果我添加了一個支持排序順序的索引(在這種情況下,是
WHERE
子句),查詢計劃可能會更改為使用它。這並不能保證,因為在某些時候優化器可能會決定執行 Key Lookup 不符合其最佳利益。CREATE INDEX ix_yourmom ON dbo.Users (Reputation);
但在這種情況下,我返回的行數足夠少,以使 Key Lookup 具有吸引力。它有六塊腹肌和很酷的動漫髮型,或者其他什麼。
當然,如果我給排序增加更多複雜性,計劃會回到掃描聚集索引並使用排序運算符給我們排序的原始計劃。
SELECT * FROM dbo.Users AS u WHERE u.Reputation < 2 ORDER BY CAST(u.Reputation AS FLOAT) / POWER(CAST(DATEDIFF(HOUR, u.CreationDate, '2018-05-01 00:00:00.0000000') AS FLOAT) + CAST(2 AS FLOAT(53)), CAST(1.8 AS FLOAT(53)));
您通常解決此問題的方法是添加一個計算列:
ALTER TABLE dbo.Users ADD RankingSort_Literal AS CAST(Reputation AS FLOAT) / POWER(CAST(DATEDIFF(HOUR, CreationDate, '2018-05-01 00:00:00.0000000') AS FLOAT) + CAST(2 AS FLOAT(53)), CAST(1.8 AS FLOAT(53)))
但既然你說:
文字“2018-05-01 00:00:00.0000000”取決於目前時間。
這使得
DATETIME
字面量相當於 callGETDATE()
,這意味著您的計算列將不是確定性的。擁有一個不確定的計算列意味著你不能 a) 索引它 b) 持久化它。有時這沒關係,但當您嘗試按它對數據進行排序時就不行了。這裡沒有任何好處。
當然,我是在沒有深入了解的情況下說這一切:
- 其餘的查詢
- 目前指標
- 查詢計劃
- 表定義
- SQL Server 硬體和設置
可能是添加記憶體或更改查詢以在儲存過程中使用臨時表是有意義的,或者只是更改查詢以通過 TOP 選擇更少的行會有所幫助。不過,我不確定您是否能夠進行任何這些更改。
這讓我回到了最初的觀點:您應該在應用程序中進行這種排序。
希望這可以幫助!