sql - 使用 SQL 标量函数的查询如何从运行 5 分钟缩短到 5 小时
问题描述
我一直在使用此功能,该功能很常见,可以删除非字母数字字符。
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 开发人员版)
我真的很难过,我知道标量函数不是最有效的,但是运行时间的这种急剧增加让我感到困惑。
解决方案
正如我在评论中提到的,你的问题是这里的 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]';
然后您可以CROSS APPLY
在以下位置调用所述函数FROM
:
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
)方法。sql server pre 2017 的 string_agg
推荐阅读
- javascript - 在量角器中使用 xml2js
- c++ - 在二叉树中打印公共元素
- ios - 单击时在 BarButtonItem 下方显示下拉菜单(弹出窗口)
- javascript - 使用反应路由器时获取数据的最佳实践
- vue.js - 为什么在 Nuxt.js 官方文档中他们在 fetch 函数中同时使用 fetch 和 axios
- python-3.x - 使用 pip 安装“prefixscan”包时出错
- image-recognition - 认知面孔中的 personGroupId 是什么?
- serialization - protobuf 中的选项会影响消息大小吗?
- mysql - 在 NoSQL 特性方面,PostgreSQL 能做哪些 MySQL 不能做的事情?
- mysql - 选择并跳过 MySQL 结果