[转]SQL分页存储过程(not in模式、二分法实现)以及产生的问题

编程技术  /  houtizong 发布于 3年前   60
转:http://www.itphome.cn/shujukuyingyong/mssql/2010-01-27/149.html
二分法分页模式是常规分页模式的改进,但是如果排序字段有重复值时(即不是primarykey)就很有可能出现第一页和第二页内容相同,解决办法是1。用传统的分页,即去掉二分法功能,在数据量小的情况下没有效率差异。2.尽量用primarykey作为排序字段,如果不可以实现特殊功能的话可以用sql2005特有的功能,即隐藏字段(创建表或视图的时候会分配一隐含的id,即使对表进行插入删除操作,但是该字段永远是从小到大排列且唯一)

先看一下常规的分页存储过程

--非2分页 By JHF 2008*06*05
CREATE PROCEDURE [dbo].[proc_ListPage]@tblName     nvarchar(200),        ----要显示的表或多个表的连接@fldName     nvarchar(500) = '*',    ----要显示的字段列表@pageSize    int = 10,        ----每页显示的记录个数@page        int = 1,        ----要显示那一页的记录@pageCount    int = 1 output,            ----查询结果分页后的总页数@Counts    int = 1 output,                ----查询到的记录数@fldSort    nvarchar(200) = null,    ----排序字段列表或条件@Sort        bit = 1,        ----排序方法,0为升序,1为降序(如果是多字段排列Sort指代最后一个排序字段的排列顺序(最后一个排序字段不加排序标记)--程序传参如:' SortA Asc,SortB Desc,SortC ')@strCondition    nvarchar(1000) = null,    ----查询条件,不需where@ID        nvarchar(150),        ----主表的主键@Dist                 bit = 0           ----是否添加查询字段的 DISTINCT 默认0不添加/1添加ASSET NOCOUNT ONDeclare @sqlTmp nvarchar(1000)        ----存放动态生成的SQL语句Declare @strTmp nvarchar(1000)        ----存放取得查询结果总数的查询语句Declare @strID     nvarchar(1000)        ----存放取得查询开头或结尾ID的查询语句Declare @strSortType nvarchar(10)    ----数据排序规则ADeclare @strFSortType nvarchar(10)    ----数据排序规则BDeclare @SqlSelect nvarchar(50)         ----对含有DISTINCT的查询进行SQL构造Declare @SqlCounts nvarchar(50)          ----对含有DISTINCT的总数查询进行SQL构造if @Dist = 0begin    set @SqlSelect = 'select '    set @SqlCounts = 'Count(*)'endelsebegin    set @SqlSelect = 'select distinct '    set @SqlCounts = 'Count(DISTINCT '+@ID+')'endif @Sort=0begin    set @strFSortType=' ASC '    set @strSortType=' DESC 'endelsebegin    set @strFSortType=' DESC '    set @strSortType=' ASC 'end --------生成查询语句----------此处@strTmp为取得查询结果数量的语句if @strCondition is null or @strCondition=''     --没有设置显示条件begin    set @sqlTmp = @fldName + ' From ' + @tblName    set @strTmp = @SqlSelect+' @Counts='+@SqlCounts+' FROM '+@tblName    set @strID = ' From ' + @tblNameendelsebegin    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) ' + @strConditionend----取得查询结果总数量-----exec sp_executesql @strTmp,N'@Counts int out ',@Counts outdeclare @tmpCounts intif @Counts = 0    set @tmpCounts = 1else    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                set @strTmp=@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName+' from '+@tblName                        +' where '+@ID+' not in('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-1) as Varchar(20)) +' '+ @ID +' from '+@tblName                        +' order by '+ @fldSort +' '+ @strFSortType+')'                        +' order by '+ @fldSort +' '+ @strFSortType              end    else --有查询条件    begin                       set @strTmp=@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName +' from '+@tblName                    +' where '+@ID+' not in('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-1) as Varchar(20)) +' '+ @ID +' from '+@tblName                    +' Where (1>0) ' + @strCondition + ' order by '+ @fldSort +' '+ @strFSortType+')'                    +' ' + @strCondition + ' order by '+ @fldSort +' '+ @strFSortType                            end

------返回查询结果-----
exec sp_executesql @strTmp
--print @strTmp
SET NOCOUNT OFF

在比较一下二分法存储过程,当数据量很大时,原则上二分法省一半时间。

--created by JHF 2008*06*05CREATE PROCEDURE [dbo].[proc_ListPage]@tblName     nvarchar(200),        ----要显示的表或多个表的连接@fldName     nvarchar(500) = '*',    ----要显示的字段列表@pageSize    int = 10,        ----每页显示的记录个数@page        int = 1,        ----要显示那一页的记录@pageCount    int = 1 output,            ----查询结果分页后的总页数@Counts    int = 1 output,                ----查询到的记录数@fldSort    nvarchar(200) = null,    ----排序字段列表或条件@Sort        bit = 1,        ----排序方法,0为升序,1为降序(如果是多字段排列Sort指代最后一个排序字段的排列顺序(最后一个排序字段不加排序标记)--程序传参如:' SortA Asc,SortB Desc,SortC ')@strCondition    nvarchar(1000) = null,    ----查询条件,不需where@ID        nvarchar(150),        ----主表的主键@Dist       bit = 0           ----是否添加查询字段的 DISTINCT 默认0不添加/1添加ASSET NOCOUNT ONDeclare @sqlTmp nvarchar(1000)        ----存放动态生成的SQL语句Declare @strTmp nvarchar(1000)        ----存放取得查询结果总数的查询语句Declare @strID     nvarchar(1000)        ----存放取得查询开头或结尾ID的查询语句Declare @strSortType nvarchar(10)    ----数据排序规则ADeclare @strFSortType nvarchar(10)    ----数据排序规则BDeclare @SqlSelect nvarchar(50)         ----对含有DISTINCT的查询进行SQL构造Declare @SqlCounts nvarchar(50)          ----对含有DISTINCT的总数查询进行SQL构造if @Dist = 0begin    set @SqlSelect = 'select '    set @SqlCounts = 'Count(*)'endelsebegin    set @SqlSelect = 'select distinct '    set @SqlCounts = 'Count(DISTINCT '+@ID+')'endif @Sort=0begin    set @strFSortType=' ASC '    set @strSortType=' DESC 'endelsebegin    set @strFSortType=' DESC '    set @strSortType=' ASC 'end --------生成查询语句----------此处@strTmp为取得查询结果数量的语句if @strCondition is null or @strCondition=''     --没有设置显示条件begin    set @sqlTmp = @fldName + ' From ' + @tblName    set @strTmp = @SqlSelect+' @Counts='+@SqlCounts+' FROM '+@tblName    set @strID = ' From ' + @tblNameendelsebegin    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) ' + @strConditionend----取得查询结果总数量-----exec sp_executesql @strTmp,N'@Counts int out ',@Counts outdeclare @tmpCounts intif @Counts = 0    set @tmpCounts = 1else    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                 set @strTmp=@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName+' from '+@tblName                        +' where '+@ID+' not in('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-1) as Varchar(20)) +' '+ @ID +' from '+@tblName                        +' order by '+ @fldSort +' '+ @strFSortType+')'                        +' order by '+ @fldSort +' '+ @strFSortType             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                                    set @strTmp=@SqlSelect+' * from ('+@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName+' from '+@tblName                        +' where '+@ID+' not in('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-2)+@lastcount as Varchar(20)) +' '+ @ID +' from '+@tblName                        +' order by '+ @fldSort +' '+ @strSortType+')'                        +' order by '+ @fldSort +' '+ @strSortType+') AS TempTB'+' order by '+ @fldSort +' '+ @strFSortType             end    end    else --有查询条件    begin        if @pageIndex<2 or @page<=@pageIndex / 2 + @pageIndex % 2   --前半部分数据处理        begin                 set @strTmp=@SqlSelect+' top '+ CAST(@pageSize as VARCHAR(4))+' '+ @fldName +' from '+@tblName                    +' where '+@ID+' not in('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-1) as Varchar(20)) +' '+ @ID +' from '+@tblName                    +' Where (1>0) ' + @strCondition + ' order by '+ @fldSort +' '+ @strFSortType+')'                    +' ' + @strCondition + ' order by '+ @fldSort +' '+ @strFSortType                         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>0) '+ @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+' not in('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-2)+@lastcount as Varchar(20)) +' '+ @ID +' from '+@tblName                        +' where (1>0) '+ @strCondition +' order by '+ @fldSort +' '+ @strSortType+')'                        + @strCondition +' order by '+ @fldSort +' '+ @strSortType+') AS TempTB'+' order by '+ @fldSort +' '+ @strFSortType         end        end

------返回查询结果-----
exec sp_executesql @strTmp
--print @strTmp
SET NOCOUNT OFF
--由于项目使用的GUID为主键字段,这里使用了not in这样的查询

--调用方法列子:C#中

SqlParameter[] paras = new SqlParameter[8];        paras[0] = new SqlParameter("@tblname", "news");        paras[1] = new SqlParameter("@pagesize", PageSize);        paras[2] = new SqlParameter("@page", CurPage);        paras[3] = new SqlParameter("@fldsort", "convert(datetime,rtrim(repubdate))");        paras[4] = new SqlParameter("@strCondition", strCondition);        paras[5] = new SqlParameter("@id", "id");        paras[6] = new SqlParameter("@PageCount", 0);        paras[7] = new SqlParameter("@Counts", 0);        SqlCommand cmd = db.createCmd(conn, "proc_listpage", paras);        cmd.Parameters["@PageCount"].Direction = ParameterDirection.Output;   //设置页数变量为输出类型        cmd.Parameters["@Counts"].Direction = ParameterDirection.Output;//设置总记录数为输出类型        ds = db.getDataSet(cmd);        DataList1.DataKeyField = "compid";        DataList1.DataSource = ds.Tables[0];        DataList1.DataBind();        conn.Close();         int pgcnt=Convert.ToInt32(cmd.Parameters["@PageCount"].Value);//获取两个输出类型变量        int totalcnt=Convert.ToInt32(cmd.Parameters["@Counts"].Value);

--调用方法列子:asp中

<%   '系统数据配置   dim adCmdSPStoredProc   dim adParamReturnValue   dim adParaminput     dim adParamOutput   dim adBigInt   dim adBinary   dim adBoolean   dim adChar     dim adDBTimeStamp   dim adEmpty   dim adInteger   dim adSmallInt   dim adTinyInt   dim adVarChar     adCmdSPStoredProc = 4   adParamReturnValue = 4   adParaminput = 1   adParamOutput = 2   adBigInt=20     adBinary=128    adBoolean=11    adChar=129    adDBTimeStamp=135    adEmpty=0      adInteger=3    adSmallInt=2    adTinyInt=16    adVarChar=200      dim compid,typeid,curpage   dim tblName_varchar,fldName_varchar,pagesize_int,page_int,pagecount_int   dim counts_int,fldSort_varchar,sort_bit,strCondition_varchar,ID_varchar,Dist_bit     curpage=request("Page")   if curpage="" then    curpage="1"   end if///传参   set cmd = Server.CreateObject("ADODB.Command")   set rs=server.CreateObject("adodb.recordset")   cmd.ActiveConnection = conn   cmd.CommandText = "proc_ListPage"   cmd.CommandType = adCmdSPStoredProc   cmd.prepared=true     cmd.parameters.append cmd.createparameter("@tblName",adVarChar,adParaminput,200,tblName_varchar)   cmd.parameters.append cmd.createparameter("@tblName",adVarChar,adParaminput,200,fldName_varchar)   cmd.parameters.append cmd.createparameter("@pageSize",adInteger,adParaminput,4,pagesize_int)   cmd.parameters.append cmd.createparameter("@page",adInteger,adParaminput,4,page_int)   cmd.parameters.append cmd.createparameter("@pageCount",adInteger,adParamoutput,4,pagecount_int)   cmd.parameters.append cmd.createparameter("@Counts",adInteger,adParamoutput,4,counts_int)   cmd.parameters.append cmd.createparameter("@fldSort",adVarChar,adParaminput,200,fldSort_varchar)   cmd.parameters.append cmd.createparameter("@sort",adBoolean,adParaminput,1,sort_bit)   cmd.parameters.append cmd.createparameter("@strCondition",adVarChar,adParaminput,1000,strCondition_varchar)   cmd.parameters.append cmd.createparameter("@ID",adVarChar,adParaminput,150,ID_varchar)   cmd.parameters.append cmd.createparameter("@Dist",adBoolean,adParaminput,1,Dist_bit)     set rs=Server.CreateObject("ADODB.recordset")   %>

设置参数值是非常必要的。

第三个是2005下包含隐含编号的存储过程。

CREATE PROCEDURE [dbo].[Pro_ListPage]@Tables varchar(1000), --表名,多红表是请使用 tA a inner join tB b On a.AID = b.AID@PK varchar(100),    --主键,可以带表头 a.AID@Sort varchar(200) = '', --排序字段@PageNumber int = 1,    --开始页码@PageSize int = 10,        --页大小@Fields varchar(1000) = '*',--读取字段@Filter varchar(1000) = NULL,--Where条件@Group varchar(1000) = NULL,  --分组@isCount bit = 0     --1     --是否获得总记录数AS----select * from GL_NEWS order by GN_UPDATE_DATE DESC--exec Pg_Paging @Tables = 'tb_NewsInfo', @PK = 'News_ID', @Sort = 'News_ID DESC', @PageNumber = 2, @PageSize = 15,@Fields = '*', @Group = '', @isCount = 0DECLARE @strFilter varchar(2000)declare @sql varchar(8000)IF @Filter IS NOT NULL AND @Filter != ''  BEGIN    SET @strFilter = ' WHERE ' + @Filter + ' '  ENDELSE  BEGIN    SET @strFilter = ''  ENDif @isCount = 1 --只获得记录条数    begin         set @sql = 'SELECT   Count(*) FROM ' + @Tables + @strFilter      endelsebeginif @Sort = ''   set @Sort = @PK + ' DESC 'IF @PageNumber < 1   SET @PageNumber = 1if @PageNumber = 1 --第一页提高性能begin   set @sql = 'select top ' + str(@PageSize) +' '+@Fields+ '   from ' + @Tables + ' ' + @strFilter + ' ORDER BY  '+ @Sortend else  begin   DECLARE @START_ID varchar(50)DECLARE @END_ID varchar(50)SET @START_ID = convert(varchar(50),(@PageNumber - 1) * @PageSize + 1)SET @END_ID = convert(varchar(50),@PageNumber * @PageSize)    set @sql =  ' SELECT '+@Fields+ '    FROM (SELECT ROW_NUMBER() OVER(ORDER BY '+@Sort+') AS rownum,      '+@Fields+ '       FROM '+@Tables+') AS D    WHERE rownum BETWEEN '+@START_ID+' AND ' +@END_ID +' ORDER BY '+@Sort  ENDEND
--print @sql

EXEC(@sql)

其用法相同,其调用可以封装为一个函数。

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!

留言需要登陆哦

技术博客集 - 网站简介:
前后端技术:
后端基于Hyperf2.1框架开发,前端使用Bootstrap可视化布局系统生成

网站主要作用:
1.编程技术分享及讨论交流,内置聊天系统;
2.测试交流框架问题,比如:Hyperf、Laravel、TP、beego;
3.本站数据是基于大数据采集等爬虫技术为基础助力分享知识,如有侵权请发邮件到站长邮箱,站长会尽快处理;
4.站长邮箱:[email protected];

      订阅博客周刊 去订阅

文章归档

文章标签

友情链接

Auther ·HouTiZong
侯体宗的博客
© 2020 zongscan.com
版权所有ICP证 : 粤ICP备20027696号
PHP交流群 也可以扫右边的二维码
侯体宗的博客