我想在SQL Server中使用split函数。我遇到了这个帖子:Cannot find either column "dbo" or the user-defined function or aggregate "dbo.Splitfn", or the name is ambiguous
并且我觉得使用索引等进行了太多计算。我写了这个函数:
ALTER FUNCTION [dbo].[Split]
(
@Data varchar(8000),
@Delimter char(1) = ','
)
RETURNS @RetVal TABLE
(
Data varchar(8000)
)
AS
Begin
Set @Data = RTrim(Ltrim(IsNull(@Data,'')))
Set @Delimter = IsNull(@Delimter,',')
If Substring(@Data,Len(@Data),1) <> @Delimter
Begin
Set @Data = @Data + @Delimter
End
Declare @Len int = Len(@Data)
Declare @index int = 1
Declare @Char char(1) = ''
Declare @part varchar(8000) = ''
While @index <= @Len
Begin
Set @Char = Substring(@Data,@index,1)
If @Char = @Delimter And @part <> ''
Begin
Insert into @RetVal Values (@part)
Set @part = ''
End
Else
Begin
Set @part = @part + @Char
End
Set @index = @index + 1
End
RETURN;
End
有人评论哪一个有效吗?我将过多地使用此函数来分割我的一个报废应用程序的数据,我希望这是有效的。另外请提一下你是如何衡量它的效率的。
答案 0 :(得分:2)
关于不同字符串拆分方法及其效率的一些讨论,我倾向于试图让人们停止尝试在T-SQL中执行此操作。你可以花费数小时与低效的功能进行斗争,试图从它们中挤出几微秒,但这是徒劳的。在这项任务中,T-SQL本质上很慢,而且通过使用CLR(2005)或表值参数(TVP)(2008+),更好地走出T-SQL。我最近发表了一个关于此的三部分系列,可能值得一读,我怀疑你会得出我所做的相同结论(CLR很好,TVP更好,所有T-SQL方法看起来都比较傻):
http://www.sqlperformance.com/2012/07/t-sql-queries/split-strings
http://www.sqlperformance.com/2012/08/t-sql-queries/splitting-strings-follow-up
http://www.sqlperformance.com/2012/08/t-sql-queries/splitting-strings-now-with-less-t-sql
另请注意您是如何衡量效率的。
好吧,你可以做我在这些文章中做的,在你运行每个测试之前和之后选择SYSDATETIME()
,然后计算差异。您还可以在每次测试之前和之后登录到表格,或使用Profiler捕获或围绕您的测试:
SET STATISTICS TIME ON;
PRINT 'Test 1';
-- do test 1 here
PRINT 'Test 2';
-- do test 2 here
SET STATISTICS TIME OFF;
您将在消息窗格中获得输出,如:
Test 1
SQL Server execution times:
CPU time: 247 ms, elapsed time: 345 ms
Test 2
SQL Server execution times:
CPU time: 332 ms, elapsed time: 421 ms
最后,您可以使用我们的免费工具SQL Sentry Plan Explorer。 (免责声明:我为SQL Sentry工作。)
您可以将任何查询提供给计划资源管理器,生成实际计划,除了比我的Management Studio展示的showplan更具可读性的图形计划之外,您还可以获得持续时间,CPU和读取等运行时指标。因此,您可以运行两个查询并将它们并排比较,而无需执行上述任何操作:
答案 1 :(得分:2)
另一种不同的方法:
CREATE FUNCTION [dbo].[fGetTableFromList]
(
@list VARCHAR(max), @delimiter VARCHAR(10)
)
RETURNS @table TABLE
(value VARCHAR(8000)) AS
BEGIN
DECLARE @list1 VARCHAR(8000), @pos INT, @rList VARCHAR(MAX);
SET @list = LTRIM(RTRIM(@list)) + @delimiter
SET @pos = CHARINDEX(@delimiter, @list, 1)
WHILE @pos > 0
BEGIN
SET @list1 = LTRIM(RTRIM(LEFT(@list, @pos - 1)))
IF @list1 <> ''
INSERT INTO @table(value) VALUES (@list1)
SET @list = SUBSTRING(@list, @pos+1, LEN(@list))
SET @pos = CHARINDEX(@delimiter, @list, 1)
END
RETURN
END
在CPU时间内,dbo.SplitString
,dbo.Split
和我的dbo.fGetTableFromList
之间没有太大区别。我通过执行这个来了解这一点:
SET STATISTICS TIME ON;
SELECT * FROM [dbo].[Split]('Lorem ipsum dolor sit amet,...', ' ');
SELECT * FROM [dbo].[SplitString]('Lorem ipsum dolor sit amet,...', ' ');
SELECT * FROM [dbo].[fGetTableFromList]('Lorem ipsum dolor sit amet,...', ' ');
SET STATISTICS TIME OFF;
当然,可以获得更多时间执行记录,使用不同的输入进行测试,更准确地了解哪种功能可以更好地运行。
此外,您还必须注意执行计划。删除SET STATISTICS
句子并执行上述三个查询,并告诉SMSS向您显示执行计划。
只需查看“执行计划”标签中显示的摘要,并且不查看其详细信息,您就可以看到第一个Split
,花费了预计工作量的13%,第二个,SplitString
一个60%,第三个,fGetTableFromList
,再次增加13%(其余工作由SELECT
s。)。
这是“虚拟”方式,不适用于DBA。如果您需要准确或精确的基准测试,您应该尝试编写一些压力测试并提取简明的结果(如@AaronBertrand提供的链接)。
答案 2 :(得分:0)
试试这个:
CREATE function dbo.SplitString(@inputStr varchar(1000),@del varchar(5))
RETURNS @table TABLE(col varchar(100))
As
BEGIN
DECLARE @t table(col1 varchar(100))
INSERT INTO @t
select @inputStr
if CHARINDEX(@del,@inputStr,1) > 0
BEGIN
;WITH CTE as(select ROW_NUMBER() over (order by (select 0)) as id,* from @t)
,CTE1 as (
select id,ltrim(rtrim(LEFT(col1,CHARINDEX(@del,col1,1)-1))) as col,RIGHT(col1,LEN(col1)-CHARINDEX(@del,col1,1)) as rem from CTE
union all
select c.id,ltrim(rtrim(LEFT(rem,CHARINDEX(@del,rem,1)-1))) as col,RIGHT(rem,LEN(rem)-CHARINDEX(@del,rem,1))
from CTE1 c
where CHARINDEX(@del,rem,1)>0
)
INSERT INTO @table
select col from CTE1
union all
select rem from CTE1 where CHARINDEX(@del,rem,1)=0
END
ELSE
BEGIN
INSERT INTO @table
select col1 from @t
END
RETURN
END
答案 3 :(得分:0)
@AnandPhadke,
我不明白你在用CTE做什么。这完全没问题:
Create function dbo.SplitString(@inputStr varchar(1000),@del varchar(5))
RETURNS @table TABLE(col varchar(100))
As
BEGIN
DECLARE @t table(col1 varchar(100))
INSERT INTO @t
select @inputStr
if CHARINDEX(@del,@inputStr,1) > 0
BEGIN
;WITH CTE1 as (
select ltrim(rtrim(LEFT(col1,CHARINDEX(@del,col1,1)-1))) as col,RIGHT(col1,LEN(col1)-CHARINDEX(@del,col1,1)) as rem from @t
union all
select ltrim(rtrim(LEFT(rem,CHARINDEX(@del,rem,1)-1))) as col,RIGHT(rem,LEN(rem)-CHARINDEX(@del,rem,1))
from CTE1 c
where CHARINDEX(@del,rem,1)>0
)
INSERT INTO @table
select col from CTE1
union all
select rem from CTE1 where CHARINDEX(@del,rem,1)=0
END
ELSE
BEGIN
INSERT INTO @table
select col1 from @t
END
RETURN
END