我的系统做了一些相当繁重的处理,我一直在攻击性能,以便让我能够在更短的时间内运行更多的测试运行。
我有很多情况下需要调用一个UDF,比如500万行(我几乎认为没有办法解决它)。
嗯,事实证明,有一种方法可以解决这个问题,当UDF通过一组比总行数小一些的不同参数调用时,它会带来巨大的性能提升。
考虑一个UDF,它接受一组输入并返回基于复杂逻辑的结果,但是对于超过5m行的输入集,例如,只有100,000个不同的输入,因此它只会产生100,000个不同的结果元组(我的特殊情况从利率到复杂的代码分配不等,但它们都是离散的 - 这种技术的基本点是你可以通过运行SELECT DISTINCT
来简单地确定技巧是否有效。
我发现这样做:
INSERT INTO PreCalcs
SELECT param1
,param2
,dbo.udf_result(param1, param2) AS result
FROM (
SELECT DISTINCT param1, param2 FROM big_table
)
当PreCalcs被适当地编入索引时,将其与:
的组合SELECT big_table.param1
,big_table.param2
,PreCalcs.result
FROM big_table
INNER JOIN PreCalcs
ON PreCalcs.param1 = big_table.param1
AND PreCalcs.param2 = big_table.param2
你的表现得到了巨大的提升。显然,仅仅因为某些东西是确定性的,并不意味着SQL Server正在缓存过去的调用并重新使用它们,正如人们所想的那样。
你唯一需要注意的是允许NULL,然后你需要仔细修复你的连接:
SELECT big_table.param1
,big_table.param2
,PreCalcs.result
FROM big_table
INNER JOIN PreCalcs
ON (
PreCalcs.param1 = big_table.param1
OR COALESCE(PreCalcs.param1, big_table.param1) IS NULL
)
AND (
PreCalcs.param2 = big_table.param2
OR COALESCE(PreCalcs.param2, big_table.param2) IS NULL
)
希望这会有所帮助,任何类似的UDF技巧或重构查询性能都是受欢迎的。
我想问题是,为什么这样的手动缓存是必要的 - 这不是服务器知道函数是确定性的吗?如果它产生如此大的差异,并且如果UDF如此昂贵,为什么优化器不会在执行计划中执行它?
答案 0 :(得分:3)
是的,优化器不会为您手动记忆UDF。在你可以用这种方式折叠输出设置的情况下,你的技巧非常好。
如果您的UDF参数是其他表的索引,并且UDF从这些表中选择值来计算标量结果,那么可以提高性能的另一种技术是将标量UDF重写为选择结果值的表值UDF超过所有潜在参数。
当我们基于UDF查询的表受到大量插入和更新时,我使用了这种方法,涉及的查询相对复杂,并且原始UDF必须应用的行数很大。在这种情况下,您可以在性能方面取得一些重大改进,因为表值UDF只需要运行一次,并且可以作为优化的面向集合的查询运行。
答案 1 :(得分:1)
SQL Server如何知道您在500万行内有100,000个离散组合?
通过使用PreCalcs表,您只需运行超过100k行的udf而不是500万行,然后再将其展开。
现有的优化者都无法识别这些有用的信息。 标量udf是一个黑盒子。
对于更实用的解决方案,我将使用执行udf调用的计算持久列。 因此,所有查询中都可以索引/包含它。
这更适合OLTP,也许......我查询一张桌子,以多种不同的方式实时交易现金和头寸,所以这种方法适合我每次都避免udf数学开销。