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。

     
     
  • 相关阅读:
    27. Remove Element
    26. Remove Duplicates from Sorted Array
    643. Maximum Average Subarray I
    674. Longest Continuous Increasing Subsequence
    1. Two Sum
    217. Contains Duplicate
    448. Find All Numbers Disappeared in an Array
    566. Reshape the Matrix
    628. Maximum Product of Three Numbers
    UVa 1349 Optimal Bus Route Design (最佳完美匹配)
  • 原文地址:https://www.cnblogs.com/zhangq723/p/2618281.html
Copyright © 2011-2022 走看看