我有一个昂贵的标量UDF,我需要将其包含在select语句中,并使用该值来缩小where子句中的结果。 UDF从当前行获取参数,因此我不能将其存储在var中并从中进行选择。
每行运行两次UDF感觉不对:
Select someField,
someOtherField,
dbo.MyExpensiveScalarUDF(someField, someOtherField)
from someTable
where dbo.MyExpensiveScalarUDF(someField, someOtherField) in (aHandfulOfValues)
如何清理它以使该功能每行只运行一次?
答案 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。