Sql-Server

未使用“持久”列

  • March 15, 2021

我有一個具有以下結構的表:

ItemID           int
ItemType         char(1)
Language         char(2)
Localization     char(2)
Literal          nvarchar(4000)

它用於將特定的文本翻譯ItemIDLiteral一個。該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 來禁用計算列擴展。

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