1

我的系统做了一些相当繁重的处理,我一直在攻击性能,以便让我能够在更短的时间内运行更多的测试运行。

我有很多情况下,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 如此昂贵,那么优化器为什么不直接在执行计划中进行呢?

4

2 回答 2

4

是的,优化器不会为您手动记忆 UDF。在您可以以这种方式折叠输出集的情况下,您的技巧非常好。

如果您的 UDF 的参数是其他表的索引,并且 UDF 从这些表中选择值来计算标量结果,则另一种可以提高性能的技术是将标量 UDF 重写为表值 UDF,该 UDF 在您的所有表中选择结果值潜在参数。

当我们基于 UDF 查询的表进行大量插入和更新时,我使用了这种方法,所涉及的查询相对复杂,并且必须应用原始 UDF 的行数很大。在这种情况下,您可以在性能上取得一些很大的改进,因为表值 UDF 只需要运行一次,并且可以作为优化的面向集合的查询运行。

于 2009-02-03T15:44:08.177 回答
2

SQL Server 如何知道您在 500 万行中有 100,000 个离散组合?

通过使用 PreCalcs 表,您只需运行 udf 超过 100k 行而不是 500 万行,然后再次展开。

现有的优化器无法预测这些有用的信息。标量 udf 是一个黑盒子。

对于更实用的解决方案,我将使用一个计算的、持久的列来执行 udf 调用。所以它在所有查询中都可用,可以被索引/包含。

这可能更适合 OLTP... 我查询一个表以通过多种不同方式实时获取交易现金和头寸,因此这种方法适合我避免每次都产生 udf 数学开销。

于 2009-02-03T14:40:43.873 回答