不使用“持久化”列

got*_*tqn 2 sql-server computed-column sql-server-2019

我有一个具有以下结构的表:

ItemID           int
ItemType         char(1)
Language         char(2)
Localization     char(2)
Literal          nvarchar(4000)
Run Code Online (Sandbox Code Playgroud)

用于将特定的文本翻译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;
Run Code Online (Sandbox Code Playgroud)

该表只有一个索引(主键),定义如下:

ItemID, ItemType, Language, Localization
Run Code Online (Sandbox Code Playgroud)

所以查询速度更快,但我看到这个表有一些额外的读取:

Scan count    - 2     vs 12,230 
Logical reads - 3,234 vs 43,472 
Run Code Online (Sandbox Code Playgroud)

这可能是正常的,因为现在我因为该列而读取了更多数据。所以,我添加了以下索引:

(ItemID ASC, ItemType ASC, Language ASC, Localization ASC) INCLUDE ([LiteralSanitized])
Run Code Online (Sandbox Code Playgroud)

但它不被引擎使用。所以,我尝试强制引擎使用它:

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;
Run Code Online (Sandbox Code Playgroud)

但引擎正在执行以下操作:

  • 执行索引扫描(在我的新索引上)
  • 然后使用聚集索引执行嵌套循环和键查找
  • 提取literal

如果我的列是Persisted,为什么引擎继续尝试返回Literal列,因为它甚至不需要?

在此处输入图片说明

Jos*_*ell 5

您的问题最有可能的答案在于 Paul White 的这篇博文:正确的持久化计算列

缺乏优化器成本模型支持意味着 SQL Server 为标量计算分配一个小的固定成本,而不管复杂性或实现。因此,服务器通常决定重新计算存储的计算列值,而不是直接读取持久化或索引值。当计算表达式很昂贵时,这尤其痛苦,例如当它涉及调用用户定义的标量函数时。

这听起来很像 SQL Server 决定它宁愿重新计算列值而不是从磁盘读取它。

正如 Paul 所提到的,一种解决方案是使用跟踪标志 176 来禁用计算列扩展。