Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 内联表值UDF性能_Sql Server_Inline_Sql Server 2008 R2_User Defined Functions - Fatal编程技术网

Sql server 内联表值UDF性能

Sql server 内联表值UDF性能,sql-server,inline,sql-server-2008-r2,user-defined-functions,Sql Server,Inline,Sql Server 2008 R2,User Defined Functions,我正在使用SQLServer2008R2。我编写了以下表值UDF,它接受标量值this或that或两者作为参数,并返回一个包含ID、this和that列的表。当我在复杂查询中调用此函数时,我看到了糟糕的性能,但在简单查询中调用时却没有看到。我想知道是否有人对我正在做的事情有任何想法,这会让事情变得缓慢。函数定义如下: CREATE function dbo.fn_getThisThat (@this nvarchar(255), @that nvarchar(255)) RETURNS TABL

我正在使用SQLServer2008R2。我编写了以下表值UDF,它接受标量值this或that或两者作为参数,并返回一个包含ID、this和that列的表。当我在复杂查询中调用此函数时,我看到了糟糕的性能,但在简单查询中调用时却没有看到。我想知道是否有人对我正在做的事情有任何想法,这会让事情变得缓慢。函数定义如下:

CREATE function dbo.fn_getThisThat (@this nvarchar(255), @that nvarchar(255))
RETURNS TABLE
RETURN

SELECT These.this, Those.that, COALESCE(These.ID, Those.ID) as ID
FROM 
    (
    SELECT col1 as ‘this’, value1, value2, ID
    FROM (
        SELECT t1.col1, t1.col2, t1.col3, t2.col1
        FROM t1
        JOIN t2
            ON t1.col1 = t2.col1
        WHERE t2.col2 = ‘this’
        AND t1.col1 in (‘value1’, ‘value2’)
        ) SOURCE
    PIVOT (
        MAX(t1.col3) FOR t1.col1 in (value1, value2)
        ) AS pvt
    ) These
JOIN
    (
    SELECT t1.col1, t1.col2, t2.col1, t3.ID
    FROM t3
    JOIN t1
        ON t3.col1 = t1.col1
    JOIN t2
        ON t2.col1 = t1.col1
    WHERE t3.col3 = ‘value3’
    AND t1.col3 = ‘value1’
    AND t2.col3 = ‘value2’
    ) Those
WHERE that = @that
OR this = @this
传递标量参数时,以下语句的处理速度非常快(<1秒):

SELECT * FROM dbo.fn_getThisThat(scalarValue, null)
或者在相对简单的查询中,例如:

SELECT t1.col1, t1.col2, fn.This
FROM t1
CROSS APPLY dbo.fn_getThisThat(t1.col3, null)
…但是当在这样一个更复杂的查询中调用时,它会严重滞后(从~1秒的处理时间到~2:30秒)(在伪代码中:如果信息不足,请告诉我):

有人对我在第二个查询中如何降低速度有什么建议吗?我将函数更改为接受数组而不是标量参数,但这消除了我交叉应用的能力(在上一个代码片段中)。从查询分析器中我可以看出,性能受到的影响来自我的函数的交叉应用。我原以为我不会遇到RBAR,因为我的UDF不是多语句的,但也许我大错特错了

编辑:
还有一件事:查询执行计划显示函数本身只占批处理的2%;较大的查询贡献了98%,但其大部分成本来自索引查找和表扫描,而不是并行性。这让我觉得,也许函数调用并不是导致查询缓慢的主要原因,而是在一些涉及的表上缺少索引(不幸的是,我不能完全控制添加索引)。我在没有调用函数的情况下运行了查询,表扫描和索引查找仍然显示为高,但查询大约在8秒钟内完成。那么,我们回到函数…?

我认为您最好的选择是在SSMS中运行此函数,并查看您的执行计划。由于这是一个内联表值的UDF,优化器将把它合并到执行计划中,您应该能够看到事情偏离了轨道


我没有太多在交叉应用情况下使用PIVOT子查询的经验——这让我觉得可能是个问题。但是执行计划会告诉您这一点。

来自适用于Apply()的MSDN文章:

“应用运算符允许您为查询的外部表表达式返回的每一行调用表值函数。”

您的示例显示了分组方式。是否可以在对行分组后而不是在特定查询中调用函数?这将减少函数必须被调用的行数


如果做不到这一点,我的另一个建议是通过优化那里的查询,在函数本身中挤出尽可能多的性能增益。每增加一毫秒,它就会累加起来。

如前所述,外部查询中的每一行都会调用交叉应用。所以,这里的关键问题是从中返回多少行:

DECLARE @table (a, b, c)
INSERT @table (a, b, c)
SELECT (values)

SELECT t1.c1, t1.c2, t1.c3
FROM
    (
    SELECT a.c1
    FROM a
    JOIN b ON (join terms)
    WHERE a.c1 IN (SELECT a FROM @table)
    ) t1
这是将拨打到您的TVF的电话数。如果TVF对a.c2的任何值都有类似的执行时间(这是一个很大的If),那么相关的性能比较就是函数的单数执行时间*上面查询返回的行


由于原始查询的混淆/泛化,很难确定,但我怀疑您的TVF可能被消除,并且逻辑与父查询一致。如果可行的话,这可能会使您获得最佳性能。

您可能希望更改您的UDF,以便在任何地方都正确地使用由两部分组成的表名,以便向其中添加SCHEMABINDING子句。请参阅。

到目前为止,我已经能够将性能从~2:30提高到~0:17。虽然更好,但仍然不理想。我做了以下工作:

  • 将schemabinding添加到我的tv udf(谢谢你,Remus!)。这有帮助,但对性能的影响似乎不如下面的方法

  • 将主查询重新构造为在@table上连接,而不是在子查询中引用它:这似乎是最有帮助的,也是大多数性能提升的来源

我认为我剩余的延迟是由于我正在重击的大型表上缺少了一些索引,但是没有能力添加它们,我不确定我能做些什么。正如查询分析器所报告的那样,我已经将并行性成本降低到了0%,因此我认为我已经尽了我所能来处理函数调用


谢谢大家

谢谢你的建议:我不确定在应用这个函数之前我是否可以分组,因为它可以作为一个“翻译”将这个转换成那个,反之亦然。我的想法是按ID对这个和那个进行计数:据我所知,如果我先分组,我会尝试将一个整数转换成这个或那个,这是行不通的。谢谢,马克!你对我的概括是对的:我担心我把它说得太过分了。上面引用的伪代码select语句是4个语句中的一个,每个语句都调用函数。如果我要删除函数并在线应用逻辑,那么我必须在这个查询中执行4次(在整个系统中执行更多次)。我看到我正在为交叉应用程序的每一行调用函数,但考虑到它是单语句的,我认为它相对便宜。小心,QA/SSM中的执行计划百分比是估计的,而不是实际成本。特别是平行性会严重扭曲计划中的估计成本与实际成本。还有,作为你的新人,所以。。。任何你觉得有用的答案都应该是“赞成的”,你认为解决问题的答案应该是这样的。
DECLARE @table (a, b, c)
INSERT @table (a, b, c)
SELECT (values)

SELECT t1.c1, t1.c2, t1.c3
FROM
    (
    SELECT a.c1
    FROM a
    JOIN b ON (join terms)
    WHERE a.c1 IN (SELECT a FROM @table)
    ) t1