为什么我的特定用户定义函数非常慢?

时间:2013-12-06 08:58:19

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

从:X可以有零个或多个Y; X与特定的Z相关联。

我的函数计算所有X而没有Y与特定Z相关联;这是我的UDF功能:

ALTER FUNCTION [dbo].[CountXWithNoYByZ]
(
    @zUID int
)
RETURNS int
AS
BEGIN

DECLARE @result int

IF (@zUID IS NULL)
    RETURN -1;

SET @result = 
(
    SELECT COUNT(DISTINCT X.UID)
    FROM X
    INNER JOIN XZ ON X.UID = XZ.X_UID
    LEFT JOIN Y ON X.UID = Y.X_UID
    WHERE (Y.UID IS NULL) AND (XZ.Z_UID = @zUID)
)

RETURN @result
END

此功能的用法:

 DECLARE @myCount int
 SET @myCount = dbo.CountXWithNoYByZ(@zUID)
 SELECT @myCount

这个函数对我来说非常慢(当我用它作为标量函数时,在X表中约为10个记录,在X表中为~10'000个记录,在Y表中为~20'000个记录),但是当我在外面使用它时(& ; 1秒)。为什么?

注意:我知道在SELECT中使用它时UDF有些慢,因为它会针对每一行运行,但我不会在{{1}内部使用它};它将在存储过程中与SELECT组合运行一次以进行统计(与其他函数一起没有性能问题)。

编辑:好吧,我重新启动了SQL-Server,现在速度更快,但这并不意味着案例已经解决......

我是新手,但我正在尝试附上执行计划......希望它有所帮助! Estimated execution plan

1 个答案:

答案 0 :(得分:3)

我注意到的一些想法/事情:

  1. 您的查询计划包含其信息检索的所有扫描(不是任何搜索)。在性能方面,索引扫描实际上只比表扫描略好,而“聚簇索引扫描” 是表扫描。将此计划与在线运行SQL语句时获得的(可能更高效)计划进行比较会很有趣,而不是作为函数。

  2. 接受的答案)在这种情况下,查询的性能根据其运行方式而有很大差异,这可能是因为不均匀分布的数据与缓存的查询计划之间存在邪恶的联盟。一点背景:SQL Server支持称为“参数嗅探”的优化,它将根据查询中的特定值选择不同的计划。如果你说“WHERE Breed ='Pomeranian'”并且只有5个,它将使用一个计划,但如果你说“WHERE Breed ='Mutt'”并且有10,000个,它将使用不同的计划。当参数嗅探时会出现问题,导致将博美犬计划应用于mutt查询。功能与功能存储过程,如果要确保参数嗅探,则必须在每次执行时强制重新编译计划。 (但这本身就有成本,所以你应该只在由于这个特殊原因导致已知的性能问题时才这样做。)对于一个函数,你将包含一个“RECOMPILE”在相关查询中提示;对于存储过程,您可以在CREATE PROC语句中指定“WITH RECOMPILE”。关于此here

  3. 的非常好的链接
  4. 你可以为你的查询结构本身试验一些不同的方法 - 如果JOIN创建了一堆重复的结果,那么JOIN + DISTINCT可能是资源占用,而DISTINCT只会将它们全部抛弃。在您的情况下尤其如此 - 当您实际上在连接成功的任何行中积极地不感兴趣时​​,为什么要加入Y行中的每一行? “NOT EXISTS”可能更快(取决于我们正在谈论的不必要的子行数),因为它会在找到第一个匹配后立即停止尝试加入。

  5. 这样的事情:

    SELECT X.UID
    FROM X
      INNER JOIN XZ ON X.UID = XZ.X_UID
    WHERE (XZ.Z_UID = @zUID)
      AND NOT EXISTS (SELECT 1 FROM Y WHERE X.UID = Y.X_UID)
    

    您必须进行一些基准测试,因为这可能会让您的性能更差,具体取决于您的数据。