优化使用函数的 SQL Server 存储过程?

Posted

技术标签:

【中文标题】优化使用函数的 SQL Server 存储过程?【英文标题】:Optimizing SQL Server stored procedures that use functions? 【发布时间】:2012-07-25 19:32:24 【问题描述】:

我需要一些帮助来优化以下查询:

SELECT DISTINCT TOP (@NumberOfResultsRequested) dbo.FilterRecentSearchesTitles(OriginalSearchTerm) AS SearchTerms
FROM UserSearches
WHERE WebsiteID = @WebsiteID
AND LEN(OriginalSearchTerm) > 20
--AND dbo.FilterRecentSearchesTitles(OriginalSearchTerm) NOT IN (SELECT KeywordUrl FROM PopularSearchesBaseline WHERE WebsiteID = @WebsiteID)
GROUP BY OriginalSearchTerm, GeoID

如果没有注释掉的行,它运行良好。我在 UserSearches.OriginalSearchTerm、WebsiteID 和 PopularSearchesBaseline.KeywordUrl 上设置了一个索引,但是在此行中查询仍然运行缓慢。

-- 更新-- 使用的函数如下:

 ALTER FUNCTION [dbo].[FilterRecentSearchesTitles]
(
    @SearchTerm VARCHAR(512)
)

RETURNS VARCHAR(512)

AS
BEGIN
    DECLARE @Ret VARCHAR(512)

    SET @Ret = dbo.RegexReplace('[0-9]', '', REPLACE(@SearchTerm, '__s', ''), 1, 1)
    SET @Ret = dbo.RegexReplace('\.', '', @Ret, 1, 1)
    SET @Ret = dbo.RegexReplace('\s2,', ' ', @Ret, 1, 1)
    SET @Ret = dbo.RegexReplace('\sv\s', ' ', @Ret, 1, 1)

    RETURN(@Ret)
END

使用Reglar Expression Workbench 代码。

但是,正如我所提到的 - 如果没有当前注释掉的行,它运行良好。

还有其他建议吗?

【问题讨论】:

删除该功能。或者向我们展示该函数的作用。如果它对源查询中的每一行进行数据访问,我认为我们发现了您的问题。考虑将其重写为表值函数,然后 SQL Server 有机会对其进行优化。 【参考方案1】:

我猜dbo.FilterRecentSearchesTitles(OriginalSearchTerm) 是一个函数。我的建议是将其重写为table valued function,以便您可以返回一个可以加入的表。

否则,您将为尝试返回的每一行调用该函数,这将导致您的问题。

如果你不能重写函数,那为什么不创建一个只执行一次的存储过程,类似于这样:

SELECT DISTINCT TOP (@NumberOfResultsRequested) dbo.FilterRecentSearchesTitles(OriginalSearchTerm) AS SearchTerms
INTO #temp
WHERE WebsiteID = @WebsiteID


SELECT *
FROM #temp
WHERE SearchTerms NOT IN (SELECT KeywordUrl 
                            FROM PopularSearchesBaseline 
                            WHERE WebsiteID = @WebsiteID)

然后在执行一次函数后将记录放入临时表中,然后在临时表上进行选择。

【讨论】:

根据您对在每个函数上调用该函数的建议,我修改了行插入以包含此函数,将内容添加到新列中。这样,我可以避免以后对 SELECT 上的每一行都执行此操作。 +1 让我走上更好的轨道。【参考方案2】:

在这种情况下,我可能会尝试使用persisted computed column:

ALTER TABLE UserSearches ADD FilteredOriginalSearchTerm AS dbo.FilterRecentSearchesTitles(OriginalSearchTerm) PERSISTED

您可能必须像这样将WITH SCHEMABINDING 添加到您的函数(和 RegexReplace 函数)中:

ALTER FUNCTION [dbo].[FilterRecentSearchesTitles]
(
    @SearchTerm VARCHAR(512)
)

RETURNS VARCHAR(512)

WITH SCHEMABINDING -- You will need this so the function is considered deterministic

AS
BEGIN
    DECLARE @Ret VARCHAR(512)

    SET @Ret = dbo.RegexReplace('[0-9]', '', REPLACE(@SearchTerm, '__s', ''), 1, 1)
    SET @Ret = dbo.RegexReplace('\.', '', @Ret, 1, 1)
    SET @Ret = dbo.RegexReplace('\s2,', ' ', @Ret, 1, 1)
    SET @Ret = dbo.RegexReplace('\sv\s', ' ', @Ret, 1, 1)

    RETURN(@Ret)
END

这使您的查询看起来像这样:

SELECT DISTINCT TOP (@NumberOfResultsRequested) FilteredOriginalSearchTerm AS SearchTerms
FROM UserSearches
WHERE WebsiteID = @WebsiteID
AND LEN(OriginalSearchTerm) > 20
AND FilteredOriginalSearchTerm NOT IN (SELECT KeywordUrl FROM PopularSearchesBaseline WHERE WebsiteID = @WebsiteID)
GROUP BY OriginalSearchTerm, GeoID

这可能会通过连接而不是not in 或不同的索引(可能在计算列或某些覆盖索引上)来优化速度(如有必要)。此外,DISTINCTGROUP BY 对我来说有点代码味道,但它可能是合法的。

【讨论】:

是的,DISTINCT 和 GROUP BY 是对的……它们只是不同迭代的产物。感谢您的建议。【参考方案3】:

我没有在SELECT 上使用该函数,而是修改了INSERT 查询以包含该函数。这样,当我以后想要检索数据时,就可以避免为每一行调用该函数。

【讨论】:

以上是关于优化使用函数的 SQL Server 存储过程?的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server用户自定义函数(UDF)

SQL Server T—SQL 存储过程 触发器

进一步优化 SQL Server 存储过程

Sql server2014 内存优化表 本地编译存储过程

SQL Server中授予用户查看对象定义的权限

SQL Server中授予用户查看对象定义的权限