Mar*_*man 5 sql-server functions warning computed-column
我需要一个计算列来解决隐式转换问题。我有一个声明为 VARCHAR 的列,它应该只存储整数,但偶尔会被带有字符串的第三方应用程序错误填充,因此该列需要保持原样。
该表通常连接到一个将该值存储为 INT 的表。我定义了一个 PERSISTED 计算列,用于将该 VARCHAR 转换为 INT:
[iOrderNumber] AS (CONVERT([int],
case
when [ordernumber] like '#%' then (-99) when isnumeric([ordernumber])=(0) then (-99)
when CONVERT([bigint],[ordernumber],(0))>(2147483647) then (-99)
else [ordernumber]
end,(0))) PERSISTED NOT NULL
Run Code Online (Sandbox Code Playgroud)
使用计算列上的适当索引,现在可以连接到 INT 列的查询的性能显着提高。但是,即使在更新表而不是读取表时发生转换,执行计划仍会显示隐式转换警告。
我尝试在计算列定义中使用 UDF 来消除警告(我找到了一篇建议这样做的博客文章),但随后我的查询的执行时间更长,并且使用了更多的 CPU,尽管逻辑读取保持不变。但是 UDF 确实消除了警告。
除了错误之外,还有其他理由考虑警告吗?是否有理由将优化器对使用 UDF 定义的持久计算列的处理视为错误?
更重要的是,有没有办法摆脱警告,而不会招致 UDF 解决方案的性能损失?
我考虑使用触发器和包含数据的 VARCHAR 和 INT 版本的转换表,而不是使用计算列,但这似乎是很多不必要的开销。
但是,即使在更新表而不是读取表时发生转换,执行计划仍会显示隐式转换警告。
持久化计算列并不能保证将使用持久化值。优化器在使用持久值和重新计算表达式之间做出基于成本的决定,尽管还有其他因素在起作用。简化一下,这个过程是这样的:
NOEXPAND
提示进行索引和引用)。扩展为应用正交简化和优化提供了最大的机会。像大多数警告一样,影响计划的转换警告是机会性的和信息性的。它是机会主义的,因为只有当优化器遵循尝试计算合适表达式的基数的代码路径时才会添加它。这类似于“缺少索引建议”,只有在优化器尝试匹配未找到的“理想”索引定义时才会添加。换句话说:这些设施都不是基于详尽的分析。
我尝试在计算列定义中使用 UDF 来消除警告
这“有效”是因为优化器无法在优化之前将标量 UDF 扩展到其定义中。UDF 是一个“黑匣子”,具有猜测的基数和可怕的运行时性能(接近于为每个函数调用运行一个完整的单独查询的成本)。如果没有扩展,可能会产生警告的基数估计不会发生。
除了错误之外,还有其他理由考虑警告吗?
该警告表明转换可能会影响计划质量,因为对有问题的表达式执行了基数估计。编译和优化的复杂性使得无法确定基数估计是否会影响计划的最终质量,即使计算列最终被解析为持久值或索引。
因此,警告通常很有用,因为它表明内部优化决策可能已受到转换的不利影响。对于基数估计不准确以及由此产生的任何性能或资源使用影响,我总是会检查带有此警告的计划。
最后一点:问题中没有提到 SQL Server 版本,但对于 2012 及更高版本,TRY_CAST
或者TRY_CONVERT
是处理此类要求的更强大的方法。但是,此功能通常不会以某种方式影响转换警告。
在我看来,警告来自 CASE 语句的结构,其中混合了类型:INT 和 VARCHAR。
如果您将 VARCHAR 列显式 CAST 为 INT,则应删除该警告。
[iOrderNumber] AS (CONVERT([int],
case
when [ordernumber] like '#%' then (-99) when isnumeric([ordernumber])=(0) then (-99)
when CONVERT([bigint],[ordernumber],(0))>(2147483647) then (-99)
else CONVERT([int], [ordernumber], (0))
end,(0))) PERSISTED NOT NULL
Run Code Online (Sandbox Code Playgroud)