当我使用变量参数而不是常量参数时,为什么我的内联表UDF这么慢?

时间:2009-04-02 16:13:58

标签: sql-server performance sql-server-2008 user-defined-functions

我有一个表值内联UDF。我想过滤该UDF的结果以获得一个特定值。当我使用常量参数指定过滤器时,一切都很好,性能几乎是瞬间完成的。当我使用变量参数指定过滤器时,它需要大得多的时间,大约500倍的逻辑读取和20倍的持续时间。

执行计划显示,在变量参数的情况下,直到过程的最后阶段才会应用过滤器,导致多个索引扫描而不是在常量情况下执行的搜索。

我想我的问题是:为什么,因为我指定的单个过滤器参数对索引字段具有高度选择性,当该参数在变量中时,我的性能是否会进入杂草?我能做些什么吗?

它是否与查询中的分析函数有关?

以下是我的疑问:

CREATE FUNCTION fn_test()
RETURNS TABLE
WITH SCHEMABINDING
AS
    RETURN
    SELECT DISTINCT GCN_SEQNO, Drug_package_version_ID
    FROM
    (
        SELECT COALESCE(ndctbla.GCN_SEQNO, ndctblb.GCN_SEQNO) AS GCN_SEQNO,
            dpv.Drug_package_version_ID, ROW_NUMBER() OVER (PARTITION BY dpv.Drug_package_version_id ORDER BY 
                ndctbla.GCN_SEQNO DESC) AS Predicate
        FROM dbo.Drug_Package_Version dpv
            LEFT JOIN dbo.NDC ndctbla ON ndctbla.NDC = dpv.Sp_package_code
            LEFT JOIN dbo.NDC ndctblb ON ndctblb.SPC_NDC = dpv.Sp_package_code
    ) iq
    WHERE Predicate = 1
GO

GRANT SELECT ON fn_test TO public
GO

-- very fast
SELECT GCN_SEQNO
FROM dbo.fn_test()
WHERE Drug_package_version_id = 10000

GO

-- comparatively slow
DECLARE @dpvid int
SET @dpvid = 10000
SELECT GCN_SEQNO
FROM dbo.fn_test()
WHERE Drug_package_version_id = @dpvid

3 个答案:

答案 0 :(得分:2)

通过UDF创建新投影后,不能指望您的索引仍然适用于在原始表上编入索引并包含在投影中的列。当您对投影进行过滤(而不是使用索引对原始表中的UDF进行过滤)时,索引将不再适用。

您要做的是参数化要接受参数的函数。

如果您发现有太多字段需要设置参数,那么您可能需要查看索引视图,因为您可以创建投影并将其编入索引,然后对其进行查询。

答案 1 :(得分:1)

简单地说,常量很容易在计划中进行评估。局部变量不是。特别是使用排名功能和过滤器Predicate = 1

释义casparOne,您需要尽可能向内推过滤器,以便在iq派生表内的dpv.Drug_package_version_id上进行过滤。

如果您这样做,那么您也不需要PARTITION BY,因为您只有一个dpv.Drug_package_version_id。然后你可以做一个更清洁的...TOP 1 ... ORDER BY ndctbla.GCN_SEQNO DESC

答案 2 :(得分:1)

我得到的回答很好,我向他们学习,但我想我找到了一个让我满意的答案。

我认为这是使用PARTITION BY子句导致此问题。我使用自联接习语的变体重新构造了UDF:

SELECT t1.A, t1.B, t1.C
FROM T t1
    INNER JOIN
    (
        SELECT A, MAX(C) AS C
        FROM T
        GROUP BY A
    ) t2 ON t1.A = t2.A AND t1.C = t2.C

具有讽刺意味的是,这比使用特定于SQL 2008的查询更具性能,并且优化器在使用变量而不是常量加入此版本的查询时也没有问题。在这一点上,我得出结论,优化器只是不处理更新的SQL扩展以及旧的东西。作为奖励,我现在可以在我预先升级的SQL 2000平台中使用UDF。

感谢大家的帮助!