这种分割功能是高效的,还是更好的?

时间:2012-08-23 12:59:56

标签: sql sql-server sql-server-2008

我想在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

有人评论哪一个有效吗?我将过多地使用此函数来分割我的一个报废应用程序的数据,我希望这是有效的。另外请提一下你是如何衡量它的效率的。

4 个答案:

答案 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和读取等运行时指标。因此,您可以运行两个查询并将它们并排比较,而无需执行上述任何操作:

enter image description here

答案 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.SplitStringdbo.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