为啥我的特定用户定义函数很慢?
Posted
技术标签:
【中文标题】为啥我的特定用户定义函数很慢?【英文标题】:Why my particular User Defined Function is very slow?为什么我的特定用户定义函数很慢? 【发布时间】:2013-12-06 08:58:18 【问题描述】:从:X 可以有零个或多个 Y; X 与特定的 Z 相关联。
我的函数计算所有没有关联到特定 Z 的 X;这是我的 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'000 条记录和 Y 表中约 20'000 条记录约 8 秒),但当我在外面使用它时(
注意:我知道在 SELECT
中使用 UDF 时会有些慢,因为它会为每一行运行,但我不在 SELECT
中使用它;它只会在存储过程中与 SET
一起运行一次以用于统计目的(以及其他没有性能问题的函数)。
编辑:好吧,我重新启动了 SQL-Server,现在速度更快了,但这并不意味着案件解决了......
我是新手,但我正在尝试附上执行计划……希望对您有所帮助! Estimated execution plan
【问题讨论】:
我对您的主要问题是,如果您将 UDF 包装在存储过程中,为什么还要打扰它?但假设有必要,这里有一些资源可以帮助你:sqlblog.com/blogs/hugo_kornelis/archive/2012/05/20/…simple-talk.com/sql/t-sql-programming/…sqlblog.com/blogs/paul_white/archive/2011/12/23/… 您的查询计划是什么样的?你有索引吗? 我计划在多个程序中重用这个功能。是的,UID 是这些表中的索引。如前所述,仅当将代码用作函数时,它才很慢。按原样使用时速度很快。很奇怪! 给我们查询计划作为索引存在和使用的证明。 @SimonSvensson:对于 XZ 表,我只有一个聚集索引:X_UID (ASC)、Z_UID (ASC) - 主键...这可能是个问题吗? 【参考方案1】:我注意到的几个想法/事情:
您的查询计划对其信息检索进行了所有扫描(不是任何搜索)。就性能而言,索引扫描实际上仅略优于表扫描,而“聚集索引扫描”是表扫描。将此计划与您在运行 SQL 语句而不是作为函数时获得的(可能更有效的)计划进行比较会很有趣。
(已接受的答案) 在这种情况下,查询的性能会因运行方式的不同而有很大差异,这可能是由于不均匀分布的数据和缓存的查询计划之间的邪恶联盟.一点背景知识:SQL Server 支持一种称为“参数嗅探”的优化,它会根据查询中的特定值选择不同的计划。如果您说“WHERE Breed='Pomeranian'”并且只有 5 个,它将使用一个计划,但如果您说“WHERE Breed='Mutt'”并且有 10,000 个,它将使用不同的计划。当参数嗅探没有发生时,问题就来了,导致博美犬计划被应用于 mutt 查询。使用函数和存储过程,如果你想确保参数嗅探,你必须在每次执行时强制重新编译计划。 (不过,这本身是有代价的,因此您应该只在由于此特定原因而出现已知性能问题时才这样做。)对于函数,您将包含“重新编译”在相关查询中提示;对于存储过程,您可以在 CREATE PROC 语句中指定“WITH RECOMPILE”。关于这个here的非常好的链接
您可以对查询结构本身尝试一些不同的方法 - 如果 JOIN 创建了一堆重复的结果,而 DISTINCT 又将它们全部丢弃,则 JOIN + DISTINCT 可能会占用资源。这在您的情况下尤其可能 - 实际上,当您实际上对连接成功的任何行都不感兴趣时,为什么要加入这些 Y 行中的每一行? “不存在”可能会更快(取决于我们正在谈论的不必要连接的子行的数量),因为它会在找到第一个匹配项后立即停止尝试加入。
类似这样的:
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)
您必须进行一些基准测试,因为当然这也可能使您的性能更差,具体取决于您的数据。
【讨论】:
非常感谢您的想法...非常感谢...将根据您的意见尝试更多。 你是个了不起的伙伴!这个“重新编译”技巧为我做到了!请将此链接technet.microsoft.com/en-us/library/ms190439.aspx添加到您的答案中...对于理解我的奇怪行为非常有帮助。以上是关于为啥我的特定用户定义函数很慢?的主要内容,如果未能解决你的问题,请参考以下文章