SQL Server 存储过程的分页

 

None.gif SQL Server 存储过程的分页方案比拼 
None.gifSQL Server 存储过程的分页,这个问题已经讨论过几年了,很多朋友在问我,所以在此发表一下我的观点
None.gif建立表:
None.gif
None.gif
CREATE   TABLE   [ TestTable ]  (
None.gif 
[ ID ]   [ int ]   IDENTITY  ( 1 1 NOT   NULL  ,
None.gif 
[ FirstName ]   [ nvarchar ]  ( 100 ) COLLATE Chinese_PRC_CI_AS  NULL  ,
None.gif 
[ LastName ]   [ nvarchar ]  ( 100 ) COLLATE Chinese_PRC_CI_AS  NULL  ,
None.gif 
[ Country ]   [ nvarchar ]  ( 50 ) COLLATE Chinese_PRC_CI_AS  NULL  ,
None.gif 
[ Note ]   [ nvarchar ]  ( 2000 ) COLLATE Chinese_PRC_CI_AS  NULL  
None.gif
ON   [ PRIMARY ]
None.gif
GO
None.gif
None.gif 
None.gif
None.gif插入数据:(2万条,用更多的数据测试会明显一些)
None.gif
SET   IDENTITY_INSERT  TestTable  ON
None.gif
None.gif
declare   @i   int
None.gif
set   @i = 1
None.gif
while   @i <= 20000
None.gif
begin
None.gif    
insert   into  TestTable( [ id ] , FirstName, LastName, Country,Note)  values ( @i ' FirstName_XXX ' , ' LastName_XXX ' , ' Country_XXX ' , ' Note_XXX ' )
None.gif    
set   @i = @i + 1
None.gif
end
None.gif
None.gif
SET   IDENTITY_INSERT  TestTable  OFF
None.gif
None.gif 
None.gif
None.gif
-- -----------------------------------
None.gif

None.gif分页方案一:(利用Not In和SELECT TOP分页)
None.gif语句形式:
None.gif
SELECT   TOP   10   *
None.gif
FROM  TestTable
None.gif
WHERE  (ID  NOT   IN
None.gif          (
SELECT   TOP   20  id
None.gif         
FROM  TestTable
None.gif         
ORDER   BY  id))
None.gif
ORDER   BY  ID
None.gif
None.gif
None.gif
SELECT   TOP  页大小  *
None.gif
FROM  TestTable
None.gif
WHERE  (ID  NOT   IN
None.gif          (
SELECT   TOP  页大小 * 页数 id
None.gif         
FROM  表
None.gif         
ORDER   BY  id))
None.gif
ORDER   BY  ID
None.gif
None.gif
-- -----------------------------------
None.gif

None.gif分页方案二:(利用ID大于多少和SELECT TOP分页)
None.gif语句形式:
None.gif
SELECT   TOP   10   *
None.gif
FROM  TestTable
None.gif
WHERE  (ID  >
None.gif          (
SELECT   MAX (id)
None.gif         
FROM  ( SELECT   TOP   20  id
None.gif                 
FROM  TestTable
None.gif                 
ORDER   BY  id)  AS  T))
None.gif
ORDER   BY  ID
None.gif
None.gif
None.gif
SELECT   TOP  页大小  *
None.gif
FROM  TestTable
None.gif
WHERE  (ID  >
None.gif          (
SELECT   MAX (id)
None.gif         
FROM  ( SELECT   TOP  页大小 * 页数 id
None.gif                 
FROM  表
None.gif                 
ORDER   BY  id)  AS  T))
None.gif
ORDER   BY  ID
None.gif
None.gif
None.gif
-- -----------------------------------
None.gif

None.gif分页方案三:(利用SQL的游标存储过程分页)
None.gif
create    procedure  XiaoZhengGe
None.gif
@sqlstr   nvarchar ( 4000 ),  -- 查询字符串
None.gif
@currentpage   int -- 第N页
None.gif
@pagesize   int   -- 每页行数
None.gif
as
None.gif
set  nocount  on
None.gif
declare   @P1   int -- P1是游标的id
None.gif
  @rowcount   int
None.gif
exec  sp_cursoropen  @P1  output, @sqlstr , @scrollopt = 1 , @ccopt = 1 , @rowcount = @rowcount  output
None.gif
select   ceiling ( 1.0 * @rowcount / @pagesize as  总页数 -- ,@rowcount as 总行数,@currentpage as 当前页 
None.gif
set   @currentpage = ( @currentpage - 1 ) * @pagesize + 1
None.gif
exec  sp_cursorfetch  @P1 , 16 , @currentpage , @pagesize  
None.gif
exec  sp_cursorclose  @P1
None.gif
set  nocount  off
None.gif
None.gif其它的方案:如果没有主键,可以用临时表,也可以用方案三做,但是效率会低。
None.gif建议优化的时候,加上主键和索引,查询效率会提高。
None.gif
None.gif通过SQL 查询分析器,显示比较:我的结论是:
None.gif分页方案二:(利用ID大于多少和SELECT TOP分页)效率最高,需要拼接SQL语句
None.gif分页方案一:(利用Not In和SELECT TOP分页)   效率次之,需要拼接SQL语句
None.gif分页方案三:(利用SQL的游标存储过程分页)    效率最差,但是最为通用
None.gif
None.gif在实际情况中,要具体分析。
None.gif
None.gif
None.gif更多的讨论见:
None.gifhttp:
// community.csdn.net / Expert / topic / 3292 / 3292678 .xml? temp = . 1621515
None.gif
None.gif
None.gif
None.gif
None.gifTrackback: http:
// tb.blog.csdn.net / TrackBack.aspx?PostId = 103511
None.gif
None.gif
[ 点击此处收藏本文 ]    发表于 2004年09月14日  1 : 05  AM 
None.gif
None.gif
None.gif  
None.gifsquirrel_sc 发表于2004
- 09 - 28   8 : 38  PM  IP:  61.49 . 220 . *
None.gif能否给出具体的测试结果数据呢?谢谢
~
None.gif
None.gif  
None.gifQQ
' Richer 发表于2004-10-29 10:12 AM  IP: 219.239.44.*
None.gif好,总算找到这篇文章了,谢谢洪根兄
None.gif
None.gif  
None.gifSUN 发表于2004-11-09 10:49 AM  IP: 61.233.144.*
None.gifdeclare @pagesize int 
None.gifset @pagesize = 10 
None.gifselect top @pagesize * from album 
None.giforder by picid 
None.gif
None.gif这样怎么不对啊? 
None.gif老大! 
None.gifselect top 10 * from album 
None.giforder by picid 
None.gif又可以 为什么呢? 
None.gif那我怎么写存储过程啊!
None.gif
None.gif  
None.gifSUN 发表于2004-11-09 11:06 AM  IP: 61.233.144.*
None.gif那我 不是 不能写成 存储过程? 
None.gif只有写成 SQL语句放在页面上? 
None.gif这样不是会降低性能?
None.gif
None.gif  
None.gifhubinasm 发表于2004-11-21 11:51 PM  IP: 
None.gifTrackBack来自《关于数据库分页》:
None.gif
None.gifPing Back来自:blog.csdn.net
None.gif
None.gif  
None.gifsunny 发表于2004-12-02 10:25 PM  IP: 61.149.250.*
None.gif第二种办法,如果按照主键排序可以。 
None.gif
None.gif不然的话不行。 
None.gif
None.gif
None.gif  
None.gif菩提树 发表于2004-12-08 6:06 PM  IP: 219.133.133.*
None.gif很多情况下,我们可能不能按主键来排序并分页,比如,你可能需要按工资高低给20万条记录分页及排序,那你肯定只能用临时表或者游标了,游标我不熟,临时表倒是可以达到目的,可是效率不好呀 
None.gifALTER PROCEDURE dbo.GetPageRecord 
None.gif
None.gif@tblName varchar(255), -- 表名 
None.gif@fldName varchar(255), --排序字段 
None.gif@KeyField varchar(255), --主键 
None.gif@PageSize int = 10, -- 页尺寸 
None.gif@PageIndex int = 1, -- 页码 
None.gif@IsCount bit = 0, -- 返回记录总数, 非 0 值则返回 
None.gif@OrderType bit = 1, -- 设置排序类型, 非 0 值则降序 
None.gif@strWhere varchar(4000) = 
''  -- 查询条件 (注意: 不要加 where) 
None.gif
None.gifAS 
None.gifSET NOCOUNT ON 
None.gifDECLARE @PageLowerBound int 
None.gifDECLARE @PageUpperBound int 
None.gif
None.gif-- Set the page bounds 
None.gifSET @PageLowerBound = @PageSize * @PageIndex 
None.gifSET @PageUpperBound = @PageLowerBound + @PageSize + 1 
None.gif
None.gif-- Create a temp table to store the select results 
None.gifCREATE TABLE #tmp 
None.gif
None.gifRecNo int IDENTITY (1, 1) NOT NULL, 
None.gifoldid int 
None.gif
None.gif--generate record 
None.gifDeclare @Sqlstr varchar(6000) 
None.gifset @sqlstr=
' select   ' +@Keyfield+ '   from   ' +@tblname 
None.gifif(@strWhere<>
''
None.gifbegin 
None.gifset @Sqlstr=@sqlstr+
'   where  ( ' +@strWhere+ ' ) '  
None.gifend 
None.gifset @sqlstr=@sqlstr+
'   order   by   ' +@fldName 
None.gifif(@ordertype=0) 
None.gifbegin 
None.gifset @sqlstr=@sqlstr+
'   asc '  
None.gifend 
None.gifelse 
None.gifbegin 
None.gifset @sqlstr=@sqlstr+
'   desc '  
None.gifend 
None.gifset @sqlstr=
' insert   into  #tmp (oldid)  ' +@sqlstr 
None.gifexecute(@sqlstr) 
None.gifset @sqlstr=
' SELECT  TableA. *   FROM   ' +@tblname+ '  TableA (nolock), #tmp T  WHERE  T.oldid  =  TableA. ' +@keyfield+ '   AND  T.RecNo  >   ' + cast(@PageLowerBound as varchar)+ '   AND  T.RecNo  <   ' +cast(@PageUpperBound as varchar)+ '   ORDER   BY  T.RecNo '  
None.gifexecute(@sqlstr)
None.gif
None.gif  
None.gif菩提树 发表于2004-12-08 6:06 PM  IP: 219.133.133.*
None.gifALTER PROCEDURE dbo.GetPageRecord 
None.gif
None.gif@tblName varchar(255), -- 表名 
None.gif@fldName varchar(255), --排序字段 
None.gif@KeyField varchar(255), --主键 
None.gif@PageSize int = 10, -- 页尺寸 
None.gif@PageIndex int = 1, -- 页码 
None.gif@IsCount bit = 0, -- 返回记录总数, 非 0 值则返回 
None.gif@OrderType bit = 1, -- 设置排序类型, 非 0 值则降序 
None.gif@strWhere varchar(4000) = 
''  -- 查询条件 (注意: 不要加 where) 
None.gif
None.gifAS 
None.gifSET NOCOUNT ON 
None.gifDECLARE @PageLowerBound int 
None.gifDECLARE @PageUpperBound int 
None.gif
None.gif-- Set the page bounds 
None.gifSET @PageLowerBound = @PageSize * @PageIndex 
None.gifSET @PageUpperBound = @PageLowerBound + @PageSize + 1 
None.gif
None.gif-- Create a temp table to store the select results 
None.gifCREATE TABLE #tmp 
None.gif
None.gifRecNo int IDENTITY (1, 1) NOT NULL, 
None.gifoldid int 
None.gif
None.gif--generate record 
None.gifDeclare @Sqlstr varchar(6000) 
None.gifset @sqlstr=
' select   ' +@Keyfield+ '   from   ' +@tblname 
None.gifif(@strWhere<>
''
None.gifbegin 
None.gifset @Sqlstr=@sqlstr+
'   where  ( ' +@strWhere+ ' ) '  
None.gifend 
None.gifset @sqlstr=@sqlstr+
'   order   by   ' +@fldName 
None.gifif(@ordertype=0) 
None.gifbegin 
None.gifset @sqlstr=@sqlstr+
'   asc '  
None.gifend 
None.gifelse 
None.gifbegin 
None.gifset @sqlstr=@sqlstr+
'   desc '  
None.gifend 
None.gifset @sqlstr=
' insert   into  #tmp (oldid)  ' +@sqlstr 
None.gifexecute(@sqlstr) 
None.gifset @sqlstr=
' SELECT  TableA. *   FROM   ' +@tblname+ '  TableA (nolock), #tmp T  WHERE  T.oldid  =  TableA. ' +@keyfield+ '   AND  T.RecNo  >   ' + cast(@PageLowerBound as varchar)+ '   AND  T.RecNo  <   ' +cast(@PageUpperBound as varchar)+ '   ORDER   BY  T.RecNo '  
None.gifexecute(@sqlstr)
None.gif
None.gif  
None.gif3doyang 发表于2005-02-15 7:14 PM  IP: 
None.gifTrackBack来自《ASP.NET分页的处理方式》:
None.gif
None.gifPing Back来自:blog.csdn.net
None.gif
None.gif  
None.gifNill 发表于2005-03-05 1:44 AM  IP: 219.128.247.*
None.gif我改了下方案2 
None.gif
None.gifdeclare @pagesize int 
None.gifdeclare @pagenum int 
None.gifset @pagenum=3---第几页 
None.gifset @pagesize=20 ----每页显示条目数 
None.gif
None.gifdeclare @t0 varchar(10) 
None.gifdeclare @t1 varchar(10) 
None.gif
None.gifset @t0=cast(@pagesize as varchar(10)) 
None.gifset @t1=cast(@pagesize*(@pagenum-1) as varchar(10)) 
None.gif--加了个判断是否是第一页。 
None.gifif @pagenum<>1 
None.gifexec(
' SELECT   TOP   ' +@t0+  '   *   FROM  DV_board  WHERE  (boardid  > ( SELECT   MAX (boardid)  FROM  ( SELECT   TOP   ' +@t1+  '  boardid  FROM  DV_board  ORDER   BY  boardid)  AS  T))  ORDER   BY  boardID '
None.gifelse 
None.gifexec(
' SELECT   TOP   ' +@t0+  '   *   FROM  DV_board ' )
None.gif
None.gif  
None.gif曾登高 发表于2005-03-28 1:11 AM  IP: 
None.gifTrackBack来自《近日关注:系统性能提升之道--内存镜像表》:
None.gif
None.gifPing Back来自:blog.csdn.net
None.gif
None.gif  
None.gif我是好猫 发表于2005-03-29 8:49 PM  IP: 218.58.242.*
None.gif什么时候,盖子能推出个 适合分页(类似 mysql 的 LIMIT )的 新mssql版本啊 。。。。 
None.gif
None.gif  
None.gif九命鸟 发表于2005-04-20 5:10 PM  IP: 210.22.100.*
None.gif这样分页的方例都不是很通用,可以考虑用Rowcount的方法,参数@table, @fields, @filter, @orderby, @groupby, @pagenumber, @pagesize,在SP中形成SQL语句。
None.gif
None.gif  
None.gif随身影子 发表于2005-04-22 12:48 PM  IP: 219.137.167.*
None.gif如果输出按字段AA排序,并且 AA 是索引的话,用游标是最高效率的。用游标分两次分别取到Begin和End点的AA的值就可以搞定。 
None.gif
None.gif
None.gif  
None.gif王者归来 发表于2005-06-01 10:54 PM  IP: 61.186.252.*
None.gif两个问题:1.用方法2,如何选择第一页的数据,pagesize和pagecount分别怎么传? 2.如何实现主从表数据的分页?
None.gif
None.gif  
None.gif王者归来 发表于2005-06-01 10:57 PM  IP: 61.186.252.*
None.gifTo 九命鸟:老兄说的这种方法哪里可以找到参考?
None.gif
None.gif  
None.gifξσ Dicky σξ 发表于2005-06-18 10:36 AM  IP: 
None.gifTrackBack来自《SQL Server 存储过程的分页方案比拼》:
None.gif
None.gifPing Back来自:blog.csdn.net
None.gif
None.gif  
None.gifleeight 发表于2005-07-12 10:38 AM  IP: 
None.gifTrackBack来自《关于存储过程实现分页的技术》:
None.gif
None.gifPing Back来自:blog.csdn.net
None.gif
None.gif  
None.gif小灰 发表于2005-07-21 8:59 PM  IP: 61.186.252.*
None.gif看看这个效率如何?怎么改进? 
None.gifCREATE proc page 
None.gif@RecordCount int output, 
None.gif@QueryStr nvarchar(100)=
' table1 ' ,--表名、视图名、查询语句 
None.gif@PageSize int=20, --每页的大小(行数) 
None.gif@PageCurrent int=2, --要显示的页 从0开始 
None.gif@FdShow nvarchar (1000)=
' * ' , --要显示的字段列表 
None.gif@IdentityStr nvarchar (100)=
' id ' , --主键 
None.gif@WhereStr nvarchar (200)=
' 1 = 1 '
None.gif@FdOrder nvarchar(100)=
' desc '  --排序 只能取desc或者asc 
None.gifas 
None.gifdeclare 
None.gif@sql nvarchar(2000) 
None.gifset @sql = 
''  
None.gifif @WhereStr = 
''  
None.gifset @WhereStr = 
' 1 = 1 '  
None.gif
None.gifif @PageCurrent = 0 begin 
None.gifset @sql = 
' select   top   '  + cast(@PageSize as nvarchar(3)) +  '   '  + @FdShow +  '   from   '  + @QueryStr +  '   where   '  + @WhereStr +  '   order   by   '  + @IdentityStr +  '   '  + @FdOrder 
None.gifend 
None.gif
None.gifelse begin 
None.gifif upper(@FdOrder) = 
' DESC '  begin 
None.gifset @sql = 
' select   top   '  + cast(@PageSize as nvarchar(3)) +  '   '  + @FdShow +  '   from   '  + @QueryStr +  '   where   '  + @WhereStr +  '   and   '  + @IdentityStr +  ' <  (  select   min ( '  + @IdentityStr +  ' from  ( select   top   '  + cast(@PageSize*@PageCurrent as nvarchar(10)) +  '   '  + @IdentityStr +  '   from   '  + @QueryStr +  '   where   '  + @WhereStr +  '   order   by   '  + @IdentityStr +  '   desc as  t)  order   by   '  + @IdentityStr +  '   desc '  
None.gifend 
None.gifelse begin 
None.gifset @sql = 
' select   top   '  + cast(@PageSize as nvarchar(3)) +  '   '  + @FdShow +  '   from   '  + @QueryStr +  '   where   '  + @WhereStr +  '   and   '  + @IdentityStr +  ' >  (  select   max ( '  + @IdentityStr +  ' from  ( select   top   '  + cast(@PageSize*@PageCurrent as nvarchar(10)) +  '   '  + @IdentityStr +  '   from   '  + @QueryStr +  '   where   '  + @WhereStr +  '   order   by   '  + @IdentityStr +  '   asc as  t)  order   by   '  + @IdentityStr +  '   asc '  
None.gifend 
None.gifend 
None.gif--print @sql 
None.gifexecute(@sql) 
None.gif
None.gif
None.gifif(@RecordCount is null or @RecordCount<=0)begin 
None.gifdeclare @tsql nvarchar(200) 
None.gifset @tsql=N
' select   @RecordCount   =   count ( * from   '  + @QueryStr +  '   where   '  + @WhereStr 
None.gifexec sp_executesql @tsql,N
' @RecordCount   int  output ' ,@RecordCount output 
None.gifselect @Recordcount 
None.gifend 
None.gifGO 
None.gif
None.gif
None.gif  
None.gif.., 发表于2005-08-15 3:27 AM  IP: 61.186.252.*
None.gif愚以为前两种方法只对主键字段的排序有效。 
None.gifID NAME TYPEID 
None.gif1 aaa 1 
None.gif2 bbb 2 
None.gif3 ccc 3 
None.gif4 ddd 3 
None.gif5 eee 4 
None.gif6 fff 5 
None.gif
None.gif例如有如上6条数据,ID为主键,TYPEID为外部键(索引)。 
None.gif如果使用ID排序,则可得正确结果。 
None.gif但是如果使用TYPEID排序的话,每页显示3条数据,要显示第2页。则两种方法都只能显示2条记录,而实际应该是3条。 
None.gif
None.gif第3种方法,由于在下对sql server的cursor不熟悉,所以不清楚结果。
None.gif
None.gif  
None.gifcnatang 发表于2005-09-13 3:04 PM  IP: 211.100.21.*
None.gif是啊。要实现一个完美的分页好象不可能?? 
None.gif用方法2 ,我现在就碰到了 order by 非主键里的问题! 
None.gif搞了一晚上,始终没有搞掂。楼主有什么好的解决办法?
None.gif
None.gif  
None.gifzxub 发表于2005-10-13 10:21 PM  IP: 211.100.21.*
None.gif  order by非主键的话,就用主键字段+排序字段,我就不信不能排,我做C#分页插件的时候就是这么做的,没问题的。
None.gif
None.gif  
None.gif网教中国 发表于2005-12-08 10:09 AM  IP: 222.79.23.*
None.gif不错dot.gif
None.gif
None.gif  
None.giforacle 发表于2006-01-05 10:31 AM  IP: 61.154.121.*
None.gif都不好,大多数情况下不会用主键来排序,再说拼接sql这样索引就失效了,可能从效率最高掉到效率最低

转载于:https://www.cnblogs.com/szRoyman/archive/2006/01/15/317509.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值