可以内嵌表值UDF胜过在SELECT列列表中相当于标量UDF?(Can an inline tabl

2019-07-29 16:52发布

这个问题脱胎于SQLServer的:为什么要避免表值用户定义函数? 我开始问问题在一些评论和回应我的注解移动题外话。


这样你就不用看了整个讨论:我从来没有听到有人说,用户自定义函数(UDF)是慢,还是要避免。 某些链接被张贴在上面提到的,说明他们是缓慢的问题。 我仍然没有得到它,并要求一个例子。 一个例子张贴,和性能差异是巨大的。

我不能谁不知道会有这样大的性能差异的唯一的人。 我觉得这其实应该分开成一个新的问题和答案,以提高其被发现的机会。 这是这里的“问题”。 请不要关闭呢,因为我想给回答者时间张贴答案。

当然,人也应该张贴答案或示例以及。 我特别欣赏什么,这将有助于我理解为什么性能差异是如此巨大。

还请注意,我不是在谈论一个WHERE子句中使用UDF的。 我知道这如何能阻止优化做的工作。 我在性能上的差异特别感兴趣时,原来的UDF是SELECT列列表的一部分。

Answer 1:

对于标杆,让我们创建具有1M行的表:

CREATE TABLE dbo.Numbers(n INT NOT NULL PRIMARY KEY)
GO
DECLARE @i INT;
SET @i = 1;
INSERT INTO dbo.Numbers(n) SELECT 1;
WHILE @i<1024000 BEGIN
  INSERT INTO dbo.Numbers(n)
    SELECT n + @i FROM dbo.Numbers;
  SET @i = @i * 2;
END;
GO

运行简单的直列加入:

SELECT COUNT(*) FROM(
SELECT n,n+1 AS ValuePlusOne
FROM  dbo.Numbers
) AS t WHERE ValuePlusOne>0

   CPU time = 15 ms, elapsed time = 122 ms.

(1 row(s) affected)
Table 'Numbers'. Scan count 1, logical reads 3521, physical reads 3, read-ahead reads 3498, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:
   CPU time = 406 ms,  elapsed time = 951 ms.

创建一个标量UDF,只是增加了一个整数,并运行它1M次:

CREATE FUNCTION dbo.[AddOne] 
(
        @value int
)
RETURNS int
AS
BEGIN
        DECLARE @Result int
        SELECT @Result = @value + 1
        RETURN @Result
END
GO

SELECT COUNT(*) FROM(
SELECT n,dbo.AddOne(n) AS ValuePlusOne
FROM  dbo.Numbers
) AS t WHERE ValuePlusOne>0

   CPU time = 15 ms, elapsed time = 122 ms.

(1 row(s) affected)
Table 'Numbers'. Scan count 1, logical reads 3521, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:
   CPU time = 108313 ms,  elapsed time = 295072 ms.

创建一个内联UDF,这是一样快,只需添加,并运行1M次:

CREATE FUNCTION dbo.[AddOneInline] 
(
        @value int
)
RETURNS TABLE
AS
RETURN(SELECT @value + 1 AS ValuePlusOne)
GO

SELECT COUNT(*) FROM(
SELECT ValuePlusOne
FROM  dbo.Numbers
CROSS APPLY dbo.[AddOneInline](n)
) AS t WHERE ValuePlusOne>0

SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 35 ms.

(1 row(s) affected)
Table 'Numbers'. Scan count 1, logical reads 3521, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:
   CPU time = 391 ms,  elapsed time = 403 ms.

与内联一个在标量UDF的性能差异是显而易见的。



Answer 2:

好了,现在你:-)我认为我们需要更多现实的例子和疑难杂症的游戏不要开了一个棘手的问题。 那表情太老是千方百计例子会使我产生怀疑。 于是, 我重新安排查询只是有点直标量UDF跑赢查询 。 不要”相信它 - 尝试 - 这是在SQL 2K8的下2K8服务器标准一个开发框。

所有到目前为止,我们已经了解到的是,使用计算列在WHERE子句是坏的等价物。 该查询使用WHERE子句中的标量函数,而假装很选择。

SELECT COUNT(*) FROM( 
SELECT n as X,n+1 AS ValuePlusOne 
FROM  dbo.Numbers 
) AS t WHERE X>0 

表“数字”。 扫描计数1,逻辑读取3521,物理读取0,预读0,lob逻辑读取0,lob物理读取0次,lob预读0。

SQL Server的执行时间:CPU时间= 234毫秒 ,经过的时间= 228毫秒。

SELECT COUNT(*) FROM( 
SELECT n as X ,dbo.AddOne(n) AS ValuePlusOne 
FROM  dbo.Numbers 
) AS t WHERE X>0 

表“数字”。 扫描计数1,逻辑读取3521,物理读取0,预读0,lob逻辑读取0,lob物理读取0次,lob预读0。

SQL Server的执行时间:CPU时间= 202毫秒 ,经过的时间= 215毫秒。

所以,现在我们解决了如何对一些真正的信息和现实使用情况?

我给你提供2辩论:-)但请记住没有做作的陷阱。 一个TVF和标量UDF只调用它来获取的便捷方式的值,然后在查询中使用无论是作为一个值或在加入 - 没有一个人的计算什么。 有人可以构建一个表或状态如何病理资料必须是为了看LCID1和LCID2之间PERF差异?

CREATE FUNCTION [PublishingCulture]  ( @XLanguage int,
                                 @XLocale int 
) RETURNS TABLE 
AS
RETURN 
(
    select TOP 1 * from [Culture] C
    where ((C.XLang = @XLanguage and C.XLoc = @XLocale)
      or   (C.XLang = @XLanguage and C.XLoc  = 0)
      or   (C.XLang = 0 and C.XLoc = @XLocale)
      or   (C.XLang = 0 and C.XLoc = 0))
)

CREATE FUNCTION [MyLCID1] ( @XLanguage int,
                      @XLocale int )
RETURNS TABLE
AS
     RETURN ( SELECT LCID from dbo.PublishingCulture(@XLanguage, @XLocale) )

CREATE FUNCTION [MyLCID2] ( @XLanguage int,
                      @XLocale int )
RETURNS int
AS
BEGIN
    RETURN ( SELECT LCID from dbo.PublishingCulture(@XLanguage, @XLocale) )
END

select * from 
   (select Row_number() OVER(order by StartDate) as RN, Message 
    from [Ticker] as T
    join dbo.MyLCID1(@XLanguage, @XLocale) as L on T.LCID = L.LCID
    where
      Getutcdate() BETWEEN StartDate AND EndDate
   ) AS T
where RN BETWEEN @StartIndex AND (@StartIndex + @MaxItems -1)

select * from 
   (select Row_number() OVER(order by StartDate) as RN, Message 
    from [Ticker] as T
    where
        LCID = dbo.PubLCID1(@XLanguage, @XLocale) AND
   Getutcdate() BETWEEN StartDate AND EndDate
   ) AS T
where RN BETWEEN @StartIndex AND (@StartIndex + @MaxItems -1)

[文化]在XLANG,XLOC,[北京时间]有PK有PK的LCID,编号(编号是人造的)和IX上StartDare,结束日期,LCID - 尽可能接近真实的东西作为一个可以在几行获得。



文章来源: Can an inline table-valued UDF outperform the equivalent scalar UDF in a SELECT column list?