好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

SqlServerStringToTable性能测试

问题起因: 最近做的项目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个小时还未查询出数据。

备注:

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

DBA建议:

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

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

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

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

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

比如以下两种方法:

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

select id

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

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长度

执行时间

查看更多关于SqlServerStringToTable性能测试的详细内容...

  阅读:48次