视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
SqlServerStringToTable性能测试
2020-11-09 07:25:49 责编:小采
文档


问题起因: 最近做的项目DB数据量比较大(基本上一个月的数据就是10亿),而工程中Proc参数中包含有id拼接字符串,id拼接字符串格式:1,2,4,5,100,301。当数据量很小的情况下,这样做没有问题,但一旦数据量到达亿级,运行会很耗时,比如:当这样的参数id拼接

  • 问题起因:
  • 最近做的项目DB数据量比较大(基本上一个月的数据就是10亿),而工程中Proc参数中包含有id拼接字符串,id拼接字符串格式:1,2,4,5,100,301。当数据量很小的情况下,这样做没有问题,但一旦数据量到达亿级,运行会很耗时,比如:当这样的参数id拼接字符串中包含有10万个id的时候(我们实际应用中确实有这么多个id需要传到数据库,而且这样的id是从库中取出后,又经过程序的筛选后剩余的id),像这样的语句:

    Declare @IDS nvarchar(max);
    Set @IDS='10w个id用逗号分割组成的字符串';
    Select T10.TEXT,T10.Name FROM DX.M as T10 inner join dbo.StringToTable(@IDS,',') as T11 on T10.ID=T11.ID;

    执行了18个小时还未查询出数据。

    备注:

    虚拟机配置:内存:G;CPU核数:40。

  • DBA建议:
  • 我测试了下,性能还算可以。在解析5000个逗号之内性能还行,太多了,性能就急速下降了。

    最初的那个版本其实还是很常用的,性能要比改写之后的要好一些(在字符串特别长的情况下)。但是同样存在,如果字符串太长,性能急速下降的问题。

    如果真的有5W以上逗号的字符串。这个SqlServer在执行计划上会消耗很多性能。

    (自己也可以测试一下解析5000个逗号串和解析5W个字符串的差距,并不是5000字符串消耗时间*10的线性关系)

    所以应当写一个循环,一次处理一部分。

    比如以下两种方法:

    1. 每次截取前1W个字符串,解析出来之后插入到临时表,然后在解析后面的,在插入到临时表,循环处理。最后临时表和实际表进行关联。

    insert into #t1

    select id

    from dbo.stringtotable(@字符串1‍)

    insert into #t1

    select id

    from dbo.stringtotable(@字符串2)‍

    2。用in的方式,每次where条件 in 一部分。然后将结果union all起来。

    类似如下

    select id

    from table a

    where id in (@字符串1)

    union all

    select id

    from table a

    where id in (@字符串2)‍

    两种方法都可行。在字符串较短的情况下,第二种方法应该好一些。字符串较长,第一种应该好一些。

  • 测试代码:
  • Declare @MRE_MROOIDS Nvarchar(Max);
    
    Set @MRE_MROOIDS='2,4,5,396009,';
    --Set @MRE_MROOIDS='2,4,5,6,7,8,9,10,11,14,15,16,17,18,20,21,23,24,25,26,29,30';
    
    Declare @SplitChar nvarchar(2);
    Declare @EndIndex int;
    Declare @Step int;
    Declare @LastChars nvarchar(MAX);
    Declare @CurrentTempChars nvarchar(max);
    
    Set @LastChars=@MRE_MROOIDS;
    Set @Step=5000;
    Set @EndIndex=0;
    Set @SplitChar=',';
    
    IF EXISTS(SELECT * FROM tempdb.dbo.sysobjects where id=OBJECT_ID(N'tempdb..#StringToTableEntry_Temp10'))
     Begin
     Drop Table #StringToTableEntry_Temp10; 
     End 
     
    Create Table #StringToTableEntry_Temp10(ID INT);
    
    
    While(LEN(@LastChars)>@Step)
    Begin 
     Set @EndIndex= charindex(@SplitChar,@LastChars,@Step);
     
     Set @CurrentTempChars=SubString(@LastChars,0,@EndIndex);
     -- insert into temp table
     Insert Into #StringToTableEntry_Temp10
     Select Id from dbo.StringToTable2(@CurrentTempChars,',');
     
     Set @LastChars=SubString(@LastChars,@EndIndex+1,LEN(@LastChars)-@EndIndex+1)
     --Select @LastChars as LastChars;
     Set @EndIndex=@EndIndex+@Step; 
    End
    
    If LEN(@LastChars)>0 Begin
     Insert Into #StringToTableEntry_Temp10
     Select Id from dbo.StringToTable2(@LastChars,',');
    End
    
    
    Select COUNT(0) From #StringToTableEntry_Temp10

    StringToTable2函数:

    ALTER FUNCTION [dbo].[StringToTable]
    (
     @ids [nvarchar](max),
     @separator [char](1)
    )
    RETURNS @IdsTable TABLE
    (
     [Id] INT NOT NULL
    )
    AS
    BEGIN
     IF(RIGHT(@ids,1)=@separator)
     BEGIN
     SET @ids=SUBSTRING(@ids,0,LEN(@ids));
     END
    
     --下面的方式性能更好
     IF(LEN(@ids) > 0)
     BEGIN
     DECLARE @i int; 
     SET @i = CHARINDEX(@separator, @ids);
     
     WHILE @i > 0
     BEGIN
     INSERT @IdsTable VALUES(LEFT(@ids, @i - 1)); 
     SET @ids = SUBSTRING(@ids, @i + 1, LEN(@ids) - @i);
     SET @i = CHARINDEX(@separator, @ids);
     END
     
     IF(LEN(@ids) > 0)
     BEGIN
     INSERT @IdsTable VALUES(@ids);
     END
     END 
     RETURN;
    END

  • 测试结果:
  • @MRE_MROOIDS包含id记录

    @Step长度

    执行时间

    100,000

    100000

    00:09:15

    100,000

    20000

    00:03:48

    100,000

    10000

    00:01:57

    100,000

    5000

    00:01:01

    下载本文
    显示全文
    专题