我的系统做了一些相当繁重的处理,我一直在攻击性能,以便让我能够在更短的时间内运行更多的测试运行。
我有很多情况下,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 如此昂贵,那么优化器为什么不直接在执行计划中进行呢?