zoukankan      html  css  js  c++  java
  • 【转】SQL百万数据分页研究

    .net+mssql2000百万数据分页研究  

    公司一项目数据达到上百万,普通的分页方式满足不了要求,经常发生超时的错误,所以有必要对分页的方式来做下研究。

    经过搜集和测试,当前网上的成熟的分页方式有以下几种方案:

    分页存储过程1.

    以下为引用的内容:
    /*
    说明:适合单表查询 cn-web.com提供
    */

    CREATE PROCEDURE p_qcd_Pagination 
    @tblName   varchar(255),       -- 表名 
    @strGetFields varchar(1000) = '*',  -- 需要返回的列 
    @fldName varchar(255)='',      -- 排序的字段名 
    @PageSize   int = 10,          -- 页尺寸 
    @PageIndex  int = 1,           -- 页码 
    @doCount  bit = 0,   -- 返回记录总数, 非 0 值则返回 
    @OrderType bit = 0,  -- 设置排序类型, 非 0 值则降序 
    @strWhere  varchar(1500) = ''  -- 查询条件 (注意: 不要加 where) 
    AS 
    declare @strSQL   varchar(5000)       -- 主语句 
    declare @strTmp   varchar(110)        -- 临时变量 
    declare @strOrder varchar(400)        -- 排序类型 
    if @doCount != 0 
    begin 
       if @strWhere !='' 
       set @strSQL = 'select count(*) as Total from ['+ @tblName +'] where '+ @strWhere 
       else 
       set @strSQL = 'select count(*) as Total from ['+ @tblName +']'
    end 
    --以上代码的意思是如果@doCount传递过来的不是0,就执行总数统计。以下的所有代码都
    --是@doCount为0的情况 
    else 
    begin 
    if @OrderType != 0 
    begin 
       set @strTmp = '<(select min' 
    set @strOrder = ' order by ['+ @fldName +'] desc'
    --如果@OrderType不是0,就执行降序,这句很重要! 
    end 
    else 
    begin 
       set @strTmp = '>(select max'
       set @strOrder = ' order by ['+ @fldName +'] asc'
    end 
    if @PageIndex = 1 
    begin 
       if @strWhere != ''   
        set @strSQL = 'select top ' + str(@PageSize) +' '+@strGetFields+ '  from ['+ @tblName +'] where ' + @strWhere + ' ' + @strOrder 
        else 
        set @strSQL = 'select top ' + str(@PageSize) +' '+@strGetFields+ '  from ['+ @tblName +'] '+ @strOrder 
    --如果是第一页就执行以上代码,这样会加快执行速度 
    end 
    else 
    begin 
    --以下代码赋予了@strSQL以真正执行的SQL代码 
    set @strSQL = 'select top ' + str(@PageSize) +' '+@strGetFields+ '  from [' + @tblName +'] where [' + @fldName + ']' + @strTmp + '(['+ @fldName + ']) 
    from (select top ' + str((@PageIndex-1)*@PageSize) + ' ['+ @fldName + '] from ['+ @tblName +']' + @strOrder + ') as tblTmp)'+ @strOrder 
    if @strWhere != '' 
       set @strSQL = 'select top ' + str(@PageSize) +' '+@strGetFields+ '  from ['+ @tblName +'] where [' + @fldName + ']' + @strTmp + '(['+ @fldName + ']) from (select top ' + str((@PageIndex-1)*@PageSize) + ' ['+ @fldName + '] 
    from ['+ @tblName +'] where ' + @strWhere + ' ' + @strOrder + ') as tblTmp) and ' + @strWhere + ' ' + @strOrder 
    end 
    end   
    exec ( @strSQL)
    GO

    此方案只适合单表,中小数据查询,当条件出现in(1,2,3,4,5……)时,则速度明显下降。

    分页存储过程2

    以下为引用的内容:
    create PROCEDURE p_qcd_PaginationDichotomy
      (
      @tblName     nvarchar(200),        ----要显示的表或多个表的连接
      @fldName     nvarchar(500) = '*',    ----要显示的字段列表
      @pageSize    int = 10,        ----每页显示的记录个数
    @page        int = 1,        ----要显示那一页的记录
    @fldSort    nvarchar(200) = null,    ----排序字段列表或条件
    @Sort        bit = 0,        ----排序方法,0为升序,1为降序(如果是多字段排列Sort指代最后一个排序字段的排列顺序(最后一个排序字段不加排序标记)--程序传参如:' SortA Asc,SortB Desc,SortC ')
    @strCondition    nvarchar(1000) = null,    ----查询条件,不需where
    @ID        nvarchar(150),        ----主表的主键
    @Dist                 bit = 0,           ----是否添加查询字段的 DISTINCT 默认0不添加/1添加
    @pageCount    int = 1 output,            ----查询结果分页后的总页数
    @Counts    int = 1 output                ----查询到的记录数
    )
    AS
    SET NOCOUNT ON
    Declare @sqlTmp nvarchar(1000)        ----存放动态生成的SQL语句
    Declare @strTmp nvarchar(1000)        ----存放取得查询结果总数的查询语句
    Declare @strID     nvarchar(1000)        ----存放取得查询开头或结尾ID的查询语句
    Declare @strSortType nvarchar(10)    ----数据排序规则A
    Declare @strFSortType nvarchar(10)    ----数据排序规则B
    Declare @SqlSelect nvarchar(50)         ----对含有DISTINCT的查询进行SQL构造
    Declare @SqlCounts nvarchar(50)          ----对含有DISTINCT的总数查询进行SQL构造
    declare @timediff datetime  --耗时测试时间差
    select @timediff=getdate()
    if @Dist  = 0
    begin
         set @SqlSelect = 'select '
         set @SqlCounts = 'Count(*)'
    end
    else
    begin
         set @SqlSelect = 'select distinct '
         set @SqlCounts = 'Count(DISTINCT '+@ID+')'
    end
    if @Sort=0
    begin
         set @strFSortType=' ASC '
         set @strSortType=' DESC '
    end
    else
    begin
         set @strFSortType=' DESC '
         set @strSortType=' ASC '
    end
    --------生成查询语句cn-web.com提供--------
    --此处@strTmp为取得查询结果数量的语句
    if @strCondition is null or @strCondition=''     --没有设置显示条件
    begin
         set @sqlTmp =  @fldName + ' From ' + @tblName
         set @strTmp = @SqlSelect+' @Counts='+@SqlCounts+' FROM '+@tblName
         set @strID = ' From ' + @tblName
    end
    else
    begin
         set @sqlTmp = + @fldName + 'From ' + @tblName + ' where (1>0) ' + @strCondition
         set @strTmp = @SqlSelect+' @Counts='+@SqlCounts+' FROM '+@tblName + ' where (1>0) ' + @strCondition
         set @strID = ' From ' + @tblName + ' where (1>0) ' + @strCondition
    end
    ----取得查询结果总数量-----
    exec sp_executesql @strTmp,N'@Counts int out ',@Counts out
    declare @tmpCounts int
    if @Counts = 0
         set @tmpCounts = 1
    else
         set @tmpCounts = @Counts
         --取得分页总数
         set @pageCount=(@tmpCounts+@pageSize-1)/@pageSize
         /**//**//**//**当前页大于总页数 取最后一页**/
         if @page>@pageCount
             set @page=@pageCount
         --/*-----数据分页2分处理-------*/
         declare @pageIndex int --总数/页大小
         declare @lastcount int --总数%页大小 
         set @pageIndex = @tmpCounts/@pageSize
         set @lastcount = @tmpCounts%@pageSize
         if @lastcount > 0
             set @pageIndex = @pageIndex + 1
         else
             set @lastcount = @pagesize
        --//***显示分页
        if @strCondition is null or @strCondition=''     --没有设置显示条件
        begin
            if @pageIndex<2 or @page<=@pageIndex / 2 + @pageIndex % 2   --前半部分数据处理
                begin 
                    if @page=1
                        set @strTmp=@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName+' from '+@tblName                        
                            +' order by '+ @fldSort +' '+ @strFSortType
                    else
                    begin
                        if @Sort=1
                        begin                    
                        set @strTmp=@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName+' from '+@tblName
                            +' where '+@ID+' <(select min('+ @ID +') from ('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-1) as Varchar(20)) +' '+ @ID +' from '+@tblName
                            +' order by '+ @fldSort +' '+ @strFSortType+') AS TBMinID)'
                            +' order by '+ @fldSort +' '+ @strFSortType
                        end
                        else
                        begin
                        set @strTmp=@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName+' from '+@tblName
                            +' where '+@ID+' >(select max('+ @ID +') from ('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-1) as Varchar(20)) +' '+ @ID +' from '+@tblName
                            +' order by '+ @fldSort +' '+ @strFSortType+') AS TBMinID)'
                            +' order by '+ @fldSort +' '+ @strFSortType 
                        end
                    end    
                end
            else
                begin
                set @page = @pageIndex-@page+1 --后半部分数据处理
                    if @page <= 1 --最后一页数据显示                
                        set @strTmp=@SqlSelect+' * from ('+@SqlSelect+' top '+ CAST(@lastcount as VARCHAR(4))+' '+ @fldName+' from '+@tblName
                            +' order by '+ @fldSort +' '+ @strSortType+') AS TempTB'+' order by '+ @fldSort +' '+ @strFSortType 
                    else
                        if @Sort=1
                        begin
                        set @strTmp=@SqlSelect+' * from ('+@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName+' from '+@tblName
                            +' where '+@ID+' >(select max('+ @ID +') from('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-2)+@lastcount as Varchar(20)) +' '+ @ID +' from '+@tblName
                            +' order by '+ @fldSort +' '+ @strSortType+') AS TBMaxID)'
                            +' order by '+ @fldSort +' '+ @strSortType+') AS TempTB'+' order by '+ @fldSort +' '+ @strFSortType
                        end
                        else
                        begin
                        set @strTmp=@SqlSelect+' * from ('+@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName+' from '+@tblName
                            +' where '+@ID+' <(select min('+ @ID +') from('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-2)+@lastcount as Varchar(20)) +' '+ @ID +' from '+@tblName
                            +' order by '+ @fldSort +' '+ @strSortType+') AS TBMaxID)'
                            +' order by '+ @fldSort +' '+ @strSortType+') AS TempTB'+' order by '+ @fldSort +' '+ @strFSortType 
                        end
                end
        end =@pageIndex / 2 + @pageIndex % 2   --前半部分数据处理
            begin
                    if @page=1
                        set @strTmp=@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName+' from '+@tblName                        
                            +' where 1=1 ' + @strCondition + ' order by '+ @fldSort +' '+ @strFSortType
                    else if(@Sort=1)
                    begin                    
                        set @strTmp=@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName+' from '+@tblName
                            +' where '+@ID+' <(select min('+ @ID +') from ('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-1) as Varchar(20)) +' '+ @ID +' from '+@tblName
                            +' where (1=1) ' + @strCondition +' order by '+ @fldSort +' '+ @strFSortType+') AS TBMinID)'
                            +' '+ @strCondition +' order by '+ @fldSort +' '+ @strFSortType
                    end
                    else
                    begin
                        set @strTmp=@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName+' from '+@tblName
                            +' where '+@ID+' >(select max('+ @ID +') from ('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-1) as Varchar(20)) +' '+ @ID +' from '+@tblName
                            +' where (1=1) ' + @strCondition +' order by '+ @fldSort +' '+ @strFSortType+') AS TBMinID)'
                            +' '+ @strCondition +' order by '+ @fldSort +' '+ @strFSortType 
                    end           
            end
            else
            begin 
                set @page = @pageIndex-@page+1 --后半部分数据处理
                if @page <= 1 --最后一页数据显示
                        set @strTmp=@SqlSelect+' * from ('+@SqlSelect+' top '+ CAST(@lastcount as VARCHAR(4))+' '+ @fldName+' from '+@tblName
                            +' where (1=1) '+ @strCondition +' order by '+ @fldSort +' '+ @strSortType+') AS TempTB'+' order by '+ @fldSort +' '+ @strFSortType                     
                else if(@Sort=1)
                        set @strTmp=@SqlSelect+' * from ('+@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName+' from '+@tblName
                            +' where '+@ID+' >(select max('+ @ID +') from('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-2)+@lastcount as Varchar(20)) +' '+ @ID +' from '+@tblName
                            +' where (1=1) '+ @strCondition +' order by '+ @fldSort +' '+ @strSortType+') AS TBMaxID)'
                            +' '+ @strCondition+' order by '+ @fldSort +' '+ @strSortType+') AS TempTB'+' order by '+ @fldSort +' '+ @strFSortType    
                else
                        set @strTmp=@SqlSelect+' * from ('+@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName+' from '+@tblName
                            +' where '+@ID+' <(select min('+ @ID +') from('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-2)+@lastcount as Varchar(20)) +' '+ @ID +' from '+@tblName
                            +' where (1=1) '+ @strCondition +' order by '+ @fldSort +' '+ @strSortType+') AS TBMaxID)'
                            +' '+ @strCondition+' order by '+ @fldSort +' '+ @strSortType+') AS TempTB'+' order by '+ @fldSort +' '+ @strFSortType            
            end    
        end

        else --有查询条件
        begin
            if @pageIndex<2 or @page<

    ------返回查询结果-----
    --select @pageCount ,@Counts
    --select @pageCount  
    exec sp_executesql @strTmp
    select datediff(ms,@timediff,getdate()) as 耗时
    --print @strTmp
    SET NOCOUNT OFF
    GO

    此分页俗称二分法,执行效率超快,数据量达到百万后效率也没有明显下降,缺点同上,当条件出现in(1,2,3,4,5……)时,则速度明显下降。

    分页存储过程3

    以下为引用的内容:
    CREATE procedure p_qcd_Paginationcursor
    @sql nvarchar(4000), --要执行的sql语句
    @currentpage int=1, --要显示的页码
    @pagesize int=10, --每页的大小
    @pagecount int=0 out, --总页数
    @recordCount int=0 out--总记录数
    as
    set nocount on
    declare @cursor int --cursor 是游标的id

    declare @timediff datetime  --耗时测试时间差 cn-web.com提供
    select @timediff=getdate()

    exec sp_cursoropen @cursor output,@sql,@scrollopt=1,@ccopt=1,@rowcount=@pagecount output
    select @recordCount=@pagecount
    select @pagecount=ceiling(1.0*@pagecount/@pagesize) ,@currentpage=(@currentpage-1)*@pagesize+1
    select @pagecount,@currentpage
    exec sp_cursorfetch @cursor ,16,@currentpage,@pagesize
    exec sp_cursorclose @cursor
    select datediff(ms,@timediff,getdate()) as 耗时
    GO

    此方案所说是微软没有公开的游标分页,刚开始怀疑游标分页肯定浪费性能,不过经过详细测试后,发现微软就是微软,不管如何测试,不管条件如何复杂,数据量再大,性能一直保持最强劲。

    总结:在没有in(1,2,3,4,5……)之类的条件的查询时,三种方案的性能:方案3=方案2>方案1。当存在复杂条件时,方案3>方案2=方案1。

     
     
  • 相关阅读:
    [BZOJ5338][TJOI2018]xor(可持久化Trie)
    [BZOJ4592][SHOI2015]脑洞治疗仪(线段树)
    [BZOJ4571][SCOI2016]美味(贪心+主席树)
    [BZOJ4570][SCOI2016]妖怪(凸包)
    [BZOJ4569][SCOI2016]萌萌哒(倍增+并查集)
    [BZOJ4567][SCOI2016]背单词(Trie+贪心)
    [BZOJ4565][HAOI2016]字符合并(区间状压DP)
    [BZOJ4561][JLOI2016]圆的异或并(扫描线)
    [BZOJ2650]积木
    [清橙A1210]光棱坦克
  • 原文地址:https://www.cnblogs.com/zhangq723/p/2618281.html
Copyright © 2011-2022 走看看