Sql server 2005 使用需要出现在Select和Where子句中的标量UDF的最佳方法
我需要在select语句中包含一个昂贵的标量UDF,并使用该值来缩小where子句中的结果范围。UDF从当前行获取参数,因此我不能将其存储在一个变量中并从中选择 每行运行两次UDF感觉不太对劲:Sql server 2005 使用需要出现在Select和Where子句中的标量UDF的最佳方法,sql-server-2005,Sql Server 2005,我需要在select语句中包含一个昂贵的标量UDF,并使用该值来缩小where子句中的结果范围。UDF从当前行获取参数,因此我不能将其存储在一个变量中并从中选择 每行运行两次UDF感觉不太对劲: Select someField, someOtherField, dbo.MyExpensiveScalarUDF(someField, someOtherField) from someTable where dbo.MyExpensiveScalarUDF(some
Select someField,
someOtherField,
dbo.MyExpensiveScalarUDF(someField, someOtherField)
from someTable
where dbo.MyExpensiveScalarUDF(someField, someOtherField) in (aHandfulOfValues)
我如何清理它,使函数每行只运行一次 在我回答这个具体问题之前,我需要更多的细节,但我马上想到了两个基本想法: (1) 你能把它变成一个基于表的函数,加入FROM子句,然后从那里开始工作吗
(2) 查看外部应用和交叉应用连接子句。本质上,它们允许对基于表的函数进行联接,其中传递给函数的参数基于被联接的行(而不是单个调用)。BOL中有这方面的好例子。在解决这个具体问题之前,我需要更多的细节,但我马上想到了两个基本概念: (1) 你能把它变成一个基于表的函数,加入FROM子句,然后从那里开始工作吗
(2) 查看外部应用和交叉应用连接子句。本质上,它们允许对基于表的函数进行联接,其中传递给函数的参数基于被联接的行(而不是单个调用)。BOL就是一个很好的例子。仅仅因为您两次提到该函数并不意味着每行将计算两次。幸运的是,查询优化器每行只计算一次。它是否存在可能部分取决于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);
仅仅因为您碰巧两次提到该函数,并不意味着它每行将被计算两次。幸运的是,查询优化器每行只计算一次。它是否存在可能部分取决于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);
Steve是正确的-如果UDF是确定性的,查询计划可能不会重新计算相同的表达式 但是,重复自己是一个潜在的维护问题:
WITH temp AS (
Select someField,
someOtherField,
dbo.MyExpensiveScalarUDF(someField, someOtherField) AS scalar
from someTable
)
SELECT *
FROM temp
where scalar in (aHandfulOfValues)
您可以使用CTE或嵌套查询来避免它
对于任何重要大小的行集(比如50万次评估),如果可能的话,最好避免使用标量UDF。如果您在这里内联扩展它(使用CTE,您不必重复自己的操作),您可能会发现一个巨大的性能提升。标量UDF应该是最后的手段。根据我的经验,在依赖标量UDF之前,最好使用持久化计算列、内联或任何其他技术。Steve是正确的-如果UDF是确定性的,查询计划可能不会重新计算相同的表达式 但是,重复自己是一个潜在的维护问题:
WITH temp AS (
Select someField,
someOtherField,
dbo.MyExpensiveScalarUDF(someField, someOtherField) AS scalar
from someTable
)
SELECT *
FROM temp
where scalar in (aHandfulOfValues)
您可以使用CTE或嵌套查询来避免它
对于任何重要大小的行集(比如50万次评估),如果可能的话,最好避免使用标量UDF。如果您在这里内联扩展它(使用CTE,您不必重复自己的操作),您可能会发现一个巨大的性能提升。标量UDF应该是最后的手段。根据我的经验,在依赖标量UDF之前,最好使用持久化计算列、内联或任何其他技术。谢谢-这将执行时间从大约40秒减少到了不到1秒。太好了!感谢您提供有关其工作原理的反馈。这种技术/语法有名称吗?“WITH”子句中的表T称为CTE(公共表表达式),但我不知道使用CTE(或等效子查询)的技术的特定名称为了避免重复一个表达式。谢谢-这将执行时间从大约40秒降低到1秒以下。太好了!感谢您提供有关其工作原理的反馈。这种技术/语法有名称吗?“WITH”子句中的表T称为CTE(公共表表达式),但我不知道使用CTE(或等效子查询)以避免重复表达式的技术的特定名称。说得好,Cade。至少当标量计算是单个表达式时,通常最好使用返回(SELECT)的表值UDF替换标量UDF,然后使用交叉应用编写查询。一开始看起来很笨拙,但最终它开始感觉正常,而且效果很好。说得好,凯德。至少当标量计算是单个表达式时,通常最好使用返回(SELECT)的表值UDF替换标量UDF,然后使用交叉应用编写查询。一开始看起来很笨拙,但最终它开始感觉正常,并且运行良好。