![](/img/trans.png)
[英]Is it possible to write a T-SQL query for “Get largest 2” that has O(n) run-time complexity?
[英]T-SQL UDF vs full expression run-time
我试图通过在SQL SERVER中使用UDF来使查询可读,但是使用该函数时运行时间会急剧增加。
以下是我正在使用的功能:
create function DL.trim_all(@input varchar(max))
returns varchar(max)
as begin
set @input=replace(replace(replace(@input,' ',''),')',''),'(','')
return @input
end
而不是写:
SELECT
CASE WHEN replace(replace(replace([FULL_NAME_1],' ',''),')',''),'(','')=replace(replace(replace([FULL_NAME_2],' ',''),')',''),'(','') THEN 1 ELSE 0 END AS [name_match],
CASE WHEN replace(replace(replace([ADDRESS_1],' ',''),')',''),'(','')=replace(replace(replace([ADDRESS_2],' ',''),')',''),'(','') THEN 1 ELSE 0 END AS [adrs_match]
.
.
.
FROM
TABLE_1
适用于20个不同领域。
使用该功能时,运行时间为12.5分钟,而当不使用该功能时,运行时间为45秒。
有任何想法吗?
将John的想法更进一步,将标量函数转换为内联表函数,并使用交叉应用为每对列激活它-您可能会获得更好的性能,但代价是查询更加麻烦:
CREATE function DL.DoesItMatch(@s1 varchar(500),@s2 varchar(500))
returns table -- returns a table with a single row and a single column
as return
SELECT
CASE WHEN replace(replace(replace(@s1,' ',''),')',''),'(','') =
replace(replace(replace(@s2,' ',''),')',''),'(','') THEN 1 ELSE 0 END As IsMatch;
和查询:
SELECT NameMatch.IsMatch AS [name_match],
AddressMatch.IsMatch AS adrs_match
.
.
.
FROM TABLE_1
CROSS APPLY DL.DoesItMatch(FULL_NAME_1, FULL_NAME_2) As NameMatch
CROSS APPLY DL.DoesItMatch(ADDRESS_1, ADDRESS_2) As AddressMatch
无法想象巨大的提升,但是另一种方法呢
create function DL.DoesItMatch(@s1 varchar(500),@s2 varchar(500))
returns bit
as begin
return CASE WHEN replace(replace(replace(@s1,' ',''),')',''),'(','')=replace(replace(replace(@s2,' ',''),')',''),'(','') THEN 1 ELSE 0 END
end
然后将函数调用为:
SELECT
DL.DoesItMatch([FULL_NAME_1],[FULL_NAME_2]) AS [name_match],
...
FROM
TABLE_1
内联始终是要走的路。 期。 即使不考虑限制并行性的T-SQL标量UDF的方面-ITVF速度更快,所需资源(CPU,内存和IO)更少,易于维护,并且更易于故障排除/分析/配置文件/跟踪。 为了好玩,我进行了一项性能测试,将Zohar的ITVF与John的标量UDF进行了比较。 我创建了25万行,针对两者都测试了一个基本选择,然后对堆进行了另一个ORDER BY
测试以强制排序。
样本数据:
-- Sample Data
BEGIN
SET NOCOUNT ON;
IF OBJECT_ID('tempdb..#tmp','U') IS NOT NULL DROP TABLE #tmp;
SELECT TOP (250000) col1 = '('+LEFT(NEWID(),10)+')', col2 = '('+LEFT(NEWID(),10)+')'
INTO #tmp
FROM sys.all_columns a, sys.all_columns;
UPDATE #tmp SET col1 = col2 WHERE LEFT(col1,2) = LEFT(col2,2)
END
性能测试:
PRINT 'scalar, no sort'+CHAR(10)+REPLICATE('-',60);
GO
DECLARE @st DATETIME = GETDATE(), @isMatch BIT;
SELECT @isMatch = DL.DoesItMatch(t.col1,t.col2)
FROM #tmp AS t;
PRINT DATEDIFF(MS,@st,GETDATE())
GO 3
PRINT CHAR(10)+'ITVF, no sort'+CHAR(10)+REPLICATE('-',60);
GO
DECLARE @st DATETIME = GETDATE(), @isMatch BIT;
SELECT @isMatch = f.isMatch
FROM #tmp AS t
CROSS APPLY DL.DoesItMatch_ITVF(t.col1,t.col2) AS f;
PRINT DATEDIFF(MS,@st,GETDATE())
GO 3
PRINT CHAR(10)+'scalar, sorted set'+CHAR(10)+REPLICATE('-',60);
GO
DECLARE @st DATETIME = GETDATE(), @isMatch BIT;
SELECT @isMatch = DL.DoesItMatch(t.col1,t.col2)
FROM #tmp AS t
ORDER BY DL.DoesItMatch(t.col1,t.col2);
PRINT DATEDIFF(MS,@st,GETDATE())
GO 3
PRINT CHAR(10)+'ITVF, sorted set'+CHAR(10)+REPLICATE('-',60);
GO
DECLARE @st DATETIME = GETDATE(), @isMatch BIT;
SELECT @isMatch = f.isMatch
FROM #tmp AS t
CROSS APPLY DL.DoesItMatch_ITVF(t.col1,t.col2) AS f
ORDER BY f.isMatch;
PRINT DATEDIFF(MS,@st,GETDATE())
GO 3
检测结果:
scalar, no sort
------------------------------------------------------------
Beginning execution loop
844
843
840
Batch execution completed 3 times.
ITVF, no sort
------------------------------------------------------------
Beginning execution loop
270
270
270
Batch execution completed 3 times.
scalar, sorted set
------------------------------------------------------------
Beginning execution loop
937
930
936
Batch execution completed 3 times.
ITVF, sorted set
------------------------------------------------------------
Beginning execution loop
196
190
190
Batch execution completed 3 times.
因此,当不需要并行计划时,ITVF快3倍,而需要并行计划时,ITVF快5倍。 这是我测试ITVF与(标量和多语句表值UDF)的其他一些链接。
您可以在SQL Server 2019中使用Scalar UDF内联。这样,您将能够保留您编写的相同UDF,并自动获得与没有UDF的查询相同的性能。
您提供的UDF符合可嵌入性的标准,因此您的身体状况良好。 有关UDF内联功能的文档位于: https ://docs.microsoft.com/zh-cn/sql/relational-databases/user-defined-functions/scalar-udf-inlining?view = azuresqldb-current
专家提示:建议您在使用Scalar UDF内联之前,对UDF进行较小的修改。 通过避免局部变量,使其成为单个语句标量UDF。 这样,您比使用带有交叉应用的嵌入式TVF更好。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.