使用需要出现在Select和Where子句中的Scalar UDF的最佳方法

时间:2009-08-11 13:17:01

标签: sql-server-2005

我有一个昂贵的标量UDF,我需要将其包含在select语句中,并使用该值来缩小where子句中的结果。 UDF从当前行获取参数,因此我不能将其存储在var中并从中进行选择。

每行运行两次UDF感觉不对:

Select someField, 
       someOtherField, 
       dbo.MyExpensiveScalarUDF(someField, someOtherField)
from someTable
where dbo.MyExpensiveScalarUDF(someField, someOtherField) in (aHandfulOfValues)

如何清理它以使该功能每行只运行一次?

3 个答案:

答案 0 :(得分:4)

仅仅因为你偶然提到这个函数两次并不意味着每行会计算两次。幸运的是,查询优化器每行只计算一次。是否这样做可能部分取决于UDF是否具有确定性或不确定性。

看看估计的执行计划。也许你会发现你什么都不担心。

如果它被计算两次,你可以尝试这个并查看它是否改变了计划,但它仍然无法保证:

WITH T(someField,someOtherField,expensiveResult) as (
  select someField, someOtherField, dbo.MyExpensiveScalarUDF(someField, someOtherField)
  from someTable
)
  select * from T
  where expensiveResult in (thisVal,thatVal,theotherVal);

答案 1 :(得分:4)

史蒂夫是正确的 - 如果UDF是确定性的,查询计划可能不会重新评估相同的表达式。

然而,重复自己是一个潜在的维护问题:

WITH temp AS (
Select someField, 
       someOtherField, 
       dbo.MyExpensiveScalarUDF(someField, someOtherField) AS scalar
from someTable
)
SELECT *
FROM temp
where scalar in (aHandfulOfValues)

您可以使用CTE或嵌套查询来避免它。

如果可能的话,最好避免使用标量UDF来处理任何大小的行集(比如50万次评估)。如果你在这里内联扩展(并且使用CTE你不必重复自己),你可能会发现巨大的性能提升。标量UDF应该是最后的手段。根据我的经验,在依赖标量UDF之前,使用持久计算列或内联或任何其他技术会好得多。

答案 2 :(得分:3)

在我解决这个具体问题之前,我需要更多细节,但是有两个一般性的想法让我感到满意:

(1)你能把它作为一个基于表的函数,在FROM子句中加入它,并从那里开始工作吗?

(2)查看OUTER APPLY和CROSS APPLY join子句。实质上,它们允许基于表的函数的连接,其中传递给函数的参数基于要连接的行(而不是单个调用)。很好的例子是BOL。