使用 SQL 标量函数的查询如何从 5 分钟内运行到 5 小时

Posted

技术标签:

【中文标题】使用 SQL 标量函数的查询如何从 5 分钟内运行到 5 小时【英文标题】:How can a query with a SQL scalar function go from running in 5 minutes to 5 hours 【发布时间】:2020-10-20 11:21:55 【问题描述】:

我一直在使用这个功能,这个功能很常见,可以去除非字母数字字符。

ALTER Function [dbo].[RemoveNonAlphaCharacters](@Temp VarChar(1000))
Returns VarChar(1000)
AS
Begin

    Declare @KeepValues as varchar(50)
    Set @KeepValues = '%[^a-z0-9]%'
    While PatIndex(@KeepValues, @Temp) > 0
        Set @Temp = Stuff(@Temp, PatIndex(@KeepValues, @Temp), 1, '')

    Return @Temp
End

这从运行 5 分钟缩短到 5 小时。是什么原因造成的?当我从查询中删除该功能时,它会在 5 分钟内完成。此查询已运行数百次。

数据还是一样的,索引还是一样的,它们没有碎片化。 180GB左右的磁盘空间也足够,内存也很充足,基本上两次运行之间没有任何变化。我还检查了 SQL 探查器是否有任何跟踪,但我只能找到运行的内部跟踪,主要是 Microsoft Telemetry 服务(2016 开发人员版)

我真的很难过,我知道标量函数不是最有效的,但是运行时间的这种急剧增加让我感到困惑。

【问题讨论】:

因为你有一个WHILE。多行标量函数,如多行表值函数,可能非常慢。在那里也放一个 iterative 任务,RDBMS 表现不佳,你最终会得到一个缓慢的函数。您使用的是什么版本的 SQL Server,上述功能的目标是什么?是不是只保留字母和数字,去掉其他字符? 定义函数根本不花时间。 查询被处理。您的问题是询问性能,但没有关于正在运行的查询的信息。 查询非常简单,它实际上是在加入维度表以产生一组结果之前使用此函数获取单个列来去除特殊字符。改变的一件事是在运行此查询的表中添加了额外的十万行,可能是执行计划发生了变化 【参考方案1】:

正如我在 cmets 中提到的,您的问题在这里有 2 个问题:

    您有一个多行标量函数,众所周知,该函数的性能通常很差。 (即使您使用的是 SQL Server 2019,也不会内联该函数。) 您的函数中有一个WHILE,它确实性能不佳,因为 SQL 是一种基于集合的语言,因此不能很好地执行迭代过程。

我假设该函数的目的是执行它所调用的操作,并且只保留字符串中的数字和字母。由于您没有注意到版本,所以我建议使用内联表值函数。我将假设,但是,您可以访问足够新的版本来使用STRING_AGG

CREATE FUNCTION dbo.RemoveNonAlphaCharacters (@InputString varchar(1000))
RETURNS table
AS RETURN
    WITH N AS(
        SELECT N
        FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
    Tally AS(
        SELECT TOP (LEN(@InputString))
               ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
        FROM N N1, N N2, N N3),
    Chars AS(
        SELECT I,
               SUBSTRING(@InputString,I,1) AS C
        FROM Tally)
    SELECT STRING_AGG(C,'') WITHIN GROUP (ORDER BY I) AS OutputString
    FROM Chars
    WHERE C LIKE '[A-z]'
       OR C LIKE '[0-9]';

然后您可以在FROM 中使用CROSS APPLY 调用所述函数:

SELECT V.YourString,
       RNAC.OutputString
FROM (VALUES('abc 123-789'),('Apples & Pears'),('Mr O''Mally'))V(YourString)
     CROSS APPLY dbo.RemoveNonAlphaCharacters(V.YourString) RNAC;

如果您使用的不是最新版本的 SQL Server,则需要将 STRING_AGG 调用替换为“旧”FOR XML PATH(和 STUFF)方法。 string_agg for sql server pre 2017

【讨论】:

太棒了,我正在使用 SQL Server 2016 开发人员版。在您的示例中,您将如何从表中传递一个实际列来删除特殊字符? 我在答案中表明,@SQL_Novice 有一个例子 非常感谢,它的运行速度要快得多,虽然我只设法在 Azure 安装上使用它而不是 2016 版本,但必须弄清楚如何合并 XML 版本 答案中也有一个链接,@SQL_Novice。

以上是关于使用 SQL 标量函数的查询如何从 5 分钟内运行到 5 小时的主要内容,如果未能解决你的问题,请参考以下文章

从 sql 程序调用用户定义的标量函数

如何访问自定义 SQL 标量函数中的前几行?

用户定义的标量函数的 SQL*Server 常量值 - 性能

使用标量函数执行查询花费了太多时间

如何在 DB2 SQL 中创建一个给出给定行时间戳之前 5 分钟内所有行总和的列?

如何在 MS Access VB 中访问 SQL Server 标量函数