Sql-Server
未使用“持久”列
我有一個具有以下結構的表:
ItemID int ItemType char(1) Language char(2) Localization char(2) Literal nvarchar(4000)
它用於將特定的文本翻譯
ItemID
成Literal
一個。該Literal
列可能包含HTML
標籤。對於一組特定的查詢,HTML
需要刪除這些標籤,並且因為清理是使用 SQL CLR 對數千行進行的,所以我不希望在讀取時執行此類操作。所以,我添加了一個這樣的持久列:
ALTER TABLE [dbo].[table] ADD [LiteralSanitized] AS NULLIF(CAST(LTRIM(RTRIM([dbo].[fn_Utils_RemoveAllHtmlTags] ([Literal]))) AS NVARCHAR(4000)), '') PERSISTED;
該表只有一個索引(主鍵),定義如下:
ItemID, ItemType, Language, Localization
所以查詢速度更快,但我看到這個表有一些額外的讀取:
Scan count - 2 vs 12,230 Logical reads - 3,234 vs 43,472
由於該列,這可能是正常的,因為現在我讀取了更多數據。所以,我添加了以下索引:
(ItemID ASC, ItemType ASC, Language ASC, Localization ASC) INCLUDE ([LiteralSanitized])
但它不被引擎使用。所以,我嘗試強制引擎使用它:
UPDATE #temp SET [QuestionText] = PSGQ.[LiteralSanitized] FROM #temp PQD INNER JOIN [dbo].[table_with_translations] PSGQ WITH(INDEX = [the_new_index]) ON PQD.[ProtoQuestionID] = PSGQ.[ItemID] WHERE PSGQ.[ItemType] = 'Q' AND PSGQ.[Language] = @language AND RTRIM(PSGQ.[Localization]) = '' AND PSGQ.[LiteralSanitized] IS NOT NULL;
但引擎正在執行以下操作:
- 執行索引掃描(在我的新索引上)
- 然後使用聚集索引執行嵌套循環和鍵查找
- 提取
literal
列如果我的列是
Persisted
,為什麼引擎繼續嘗試返回該Literal
列,因為它甚至不需要?
您的問題最有可能的答案在於 Paul White 的這篇博文:正確持久的計算列
缺乏優化器成本模型支持意味著 SQL Server 將一個小的固定成本分配給標量計算,而不管複雜性或實現如何。因此,伺服器通常決定重新計算儲存的計算列值,而不是直接讀取持久化或索引值。當計算表達式很昂貴時,這尤其痛苦,例如當它涉及呼叫標量使用者定義函式時。
聽起來很像 SQL Server 決定它寧願重新計算列值而不是從磁碟讀取它。
正如 Paul 所提到的,一種解決方案是使用跟踪標誌 176 來禁用計算列擴展。