从 CROSS APPLY 返回的列是否只计算一次表值函数?

mon*_*ine 2 sql-server

考虑以下查询:

select Student.Id,
       case when SomeTvfResult.Num = 3 then N'foo'
            when SomeTvfResult.Num > 42 then N'bar'
            else N'baz'
       end as SomeStr
from Student
cross apply SomeTableValuedFunction(Student.Id) SomeTvfResult
Run Code Online (Sandbox Code Playgroud)

我想知道SomeTvfResult.Num每次我在case语句中引用它时是否都会重新计算它的值,或者它以某种方式缓存并仅评估一次。如果相关,您可以假设它SomeTableValuedFunction是内联 TVF。

由于我已经了解到它coalesce只是被一个case语句替换了,该语句导致给定的表达式被评估不止一次,因此每次我在查询中使用某个函数时,我都会感到担忧。

Cha*_*ace 5

您提出问题的方式表明您担心整个 TVF 会被多次评估。这当然不是这种情况,因为对于内联 TVF,内部查询被有效地粘贴到外部查询中作为派生表。

您可能关心的是 TVF 中计算列的计算是否被推迟。不幸的是,XML 查询计划没有这方面的信息(您将查看相关的COMPUTE SCALAR)。

Paul White 有一篇很好的博客文章解释了这一点。通常 SQL Server 会将计算标量的计算推迟到计划的后期,这可能成为非常复杂或计算困难的计算的问题。

例如,PATINDEXSUBSTRING(甚至REVERSE)经常串联使用来进行字符串操作。避免大量复制意大利面的常见技巧是对CROSS APPLY (VALUES)一个计算进行计算,然后在另一个计算中引用它CROSS APPLY,类似于您在程序上进行的方式。

我从查询计划中注意到优化器将整个事情折叠在一起,没有嵌套循环,没有持续扫描,这通常是正确的想法。据我了解,执行引擎随后会提取出它认为需要预先计算和存储的各种部分,以及一些它会在需要时计算的部分,但有时它可能会出错,尤其是在处理非常大的字符串时。

在这种情况下,您可以做的是使用OUTER APPLY (VALUES),这似乎会创建一个优化栅栏,从而导致具有恒定扫描的嵌套循环。不要盲目地这样做。测试它,计时,查看 CPU 时间等。