【问题标题】:Why Is My Inline Table UDF so much slower when I use variable parameters rather than constant parameters?当我使用可变参数而不是常量参数时,为什么我的内联表 UDF 慢得多?
【发布时间】:2010-10-17 03:54:07
【问题描述】:

我有一个表值的内联 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

【问题讨论】:

  • 你能把这两个执行计划都贴在某个地方,这样我就可以确切地看到 SQL Server 在这两种情况下都在做什么?

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


【解决方案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。

谢谢大家的帮助!

【讨论】:

  • 没有理由认为查询语言的新功能会带来更好的性能。他们花了很多时间调整经典表达式。
【解决方案2】:

简单地说,常数在计划中很容易评估。局部变量不是。尤其是带有排名功能和过滤器Predicate = 1

解释 casparOne,您需要将过滤器尽可能向内推,以便在 iq 派生表内过滤 dpv.Drug_package_version_id

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

【讨论】:

    【解决方案3】:

    一旦您通过 UDF 创建了新的投影,就不能期望您的索引仍会应用于在原始表中编制索引并包含在投影中的列。当您对投影进行过滤(而不是在 UDF 中针对具有索引的原始表)时,索引不再适用。

    你要做的是参数化函数以接受参数。

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

    【讨论】:

    • 几点——如果我的索引没有应用,这不会导致常量和变量情况下的性能不佳吗?此外,索引视图是一个好主意,但排名函数将其排除在查询当前状态之外。
    • @mwigdahl:我已经更新了我的帖子,以说明为什么在您过滤投影时索引不会应用(与直接在表格上相反)。另外,您可以创建一个使用排名函数的 UDF,然后从索引视图中调用它吗?然后创建在索引视图上过滤的查询?
    猜你喜欢
    • 2015-02-25
    • 1970-01-01
    • 2019-06-07
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    • 2020-06-17
    • 2019-06-22
    • 1970-01-01
    相关资源
    最近更新 更多