Username: Password:

最好的数据库分页方法-ASP教程,数据库相关
来源:作者: 发布时间:2007-12-26 02:06:37
"
  for each fldf in rsdata.fields
    response.write ""
  next
  response.write ""

   now loop through the records
  while not rsdata.eof
    response.write ""
    for each fldf in rsdata.fields
      response.write ""
    next
    response.write ""
    rsdata.movenext
  wend
  response.write "

一:一个老生常谈的问题。
    我们知道,记录集分页是数据库处理中十分常见的问题。而当我们设计到网络数据库,就是说要考虑传输带宽问题时,分页问题就每每困扰着每一个数据库程式设计人员。

二:分页问题的解决方案汇总
    说起解决的方案,每个数据库设计人员可能都会举出许多方法。但细分后,能够归为三类。一:ado纪录集分页、二:专储记录集分页、三、数据库游标分页。
 一:著名的ado纪录集分页。
     说到著名,因为这个可能是最简单和常见的分页方法了。(可能也是用的最多的)就是利用ado自带的分页功能来实现分页。
     具体流程为,数据库根据查询语句返回一个完整的纪录集。然后到客户端后有客户端的游标进行分页。他们大多数由ado自带的recordset对象就能够实现了。可能涉及到的属性有:
recordset.pagesize:每页输出纪录集的大小
recordset.absolutepage: 当前输出的页    (有以上的两个属性其实就能够完成分页输出了)
recordset.pagecount: 现在的总页数。
     这种方法好不好呢,有的人说是十分好,也有人说效率不高。其实这的根据实际的应用状况来确定,假如是单机数据库,或是局域网环境,或数据库纪录较少,则他都是很好的分页方法,而且假如不涉及到网络和更新较少的环境里,则他能够说是最好的分页方法。因为他能够先生成一个缓存记录集,以后的几页纪录都能够不通过数据库来取即可。但是,假如涉及到网络,或是更新频繁。他就不是很实用的了。

列举程式。(我们都用网络问题来考虑)
 
nowpage=request("nowpage") 当前的输出页
if nowpage="" or nowpage<1 then nowpage=1

set rs=server.createobject("adodb.recordset")
rs.cursortype=1
sql="select * from table1 "
rs.open sql,strconn (strconn为连接字段,已定义过)

rs.pagesize=20  当前页的大小
if cint(nowpage)>rs.pagecount then nowpage=rs.pagecount
rs.absolutepage=nowpage

然后输出当前一页的纪录
.............


还能够提供一下程式属性配置。
首页:nowpage=1
前页:nowpage=nowpage-1
下页:nowpage=nowpage+1
尾页:nowpage=rs.pagecount
总纪录数:rs.recordcount
总页数:rs.pagecount

二:转储纪录集分页。
这种方法诞生于网络时代,就是利用服务器端的强大处理过程,先将目标数据库存到一个临时的数据库里,并且加上一个自增字段来进行划分页面,最后将所需固定数目的纪录集传回。

长处是:只需交互一次,而且返回固定一页的纪录集。
缺点是:假如纪录集增大时则每次都需建立一个临时纪录集,也比较耗时间,但减少了网络传输量。
例子:


取自 worx 英文版的<>
isbn1861002610
关键地方我已作了中文翻译

create procedure usp_pagedauthors
@ipage int,
@ipagesize int
as
begin
-- disable row counts
set nocount on

-- declare variables
declare @istart int -- start record
declare @iend int -- end record
declare @ipagecount int -- total number of pages

-- create the temporary table
       --建立临时表。

create table #pagedauthors        (
--这个自增字段十分关键,就是靠他来完成分页标示。
id int identity,      
au_id varchar(11) not null ,
au_lname varchar(40) not null ,
au_fname varchar(20) not null ,
phone char(12) not null ,
address varchar(40) null ,
city varchar(20) null ,
state char(2) null ,
zip char(5) null ,
contract bit not null 
)

-- populate the temporary table       
--先转存到上面的这个纪录集。
insert into #pagedauthors (au_id, au_lname, au_fname, 
phone, address, city, state, zip, contract)
select au_id, au_lname, au_fname, 
phone, address, city, state, zip, contract
from authors


-- work out how many pages there are in total
select @ipagecount = count(*)
from  authors

select @ipagecount = ceiling(@ipagecount / @ipagesize) + 1

-- check the page number
if @ipage < 1
select @ipage = 1

if @ipage > @ipagecount
select @ipage = @ipagecount

-- calculate the start and end records
select @istart = (@ipage - 1) * @ipagesize
select @iend = @istart + @ipagesize + 1

-- select only those records that fall within our page
--这条sql语句就是选取固定的纪录集。

select au_id, au_lname, au_fname, 
phone, address, city, state, zip, contract
from #pagedauthors
where id > @istart
and id < @iend


drop table #pagedauthors

-- turn back on record counts
set nocount off

-- return the number of records left
return @ipagecount
end


而输出端能够用最快类型的ado"火线光标"顺次输出就可
<%
  dim cmdauthors
  dim rsdata
  dim ipage
  dim ilastpage
  dim squote

  squote = chr(34)

  get the requested data
  if request.querystring("page") = "" then
    ipage = 1
  else
    ipage = cint(request.querystring("page"))

    if ipage < 1 then
      ipage = 1
    end if
  end if

   create the objects
  set cmdauthors = server.createobject("adodb.command")
  set rsauthors = server.createobject("adodb.recordset")

  with cmdauthors
    .activeconnection = strconn
    .commandtext = "usp_pagedauthors"
    .commandtype = adcmdstoredproc

    .parameters.append .createparameter("return_value", adinteger, _
                              adparamreturnvalue)
    .parameters.append .createparameter("@ipage", adinteger, _
                              adparaminput, 8, ipage)
    .parameters.append .createparameter("@ipagesize", adinteger, _
                              adparaminput, 8, 10)

    set rsdata = .execute
  end with

   create the table
   start building the table
  response.write "
" & fldf.name & "
" & fldf.value & "

"
   now some paging controls
  sme = request.servervariables("script_name")
  response.write " first page"

   close the recordset and extract the number of records left
  rsdata.close
  ilastpage = cmdauthors.parameters("return_value")

   only give an active previous page if there are previous pages
  if ipage <= 1 then
    response.write " previous page"
  else
    response.write " previous page"
  end if

   only give an active next page if there are more pages
  if ilastpage = ipage then
    response.write " next page"
  else
    response.write " next page"
  end if

  response.write " last page"

   clean up
  set rsdata = nothing
  set cmdauthors = nothing
%>

第三种方法:服务器端游表选取纪录集的办法。
    这种办法属于很有争论的办法。
    他主要是用服务器端的游表选取纪录集,然后一次返回,也就是返回多个纪录集,每个纪录集就有一个纪录。然后用recordset.nextrecord的方法来输出每一个纪录集。
    外国许多网站对此进行过考证,因为第一:recordset.nextrecord具备这种方法的ado.游表不是最快的火线游表,第二,许多人认为采用recordset.nextrecord方法输出时其实等于和服务器端交互了一次,所以这种方法属于那种当许多人并发访问数据库时,能导致数据库访问量成倍增。。。 
             
列举程式:(作者:bigeagle)
if exists(select * from sysobjects where id = object_id("up_topiclist"))
   drop proc up_topiclist
go

create proc up_topiclist 
            @a_forumid int , @a_intdays int , @a_intpageno int , @a_intpagesize tinyint
   as
       declare @m_intrecordnumber int
       declare @m_intstartrecord  int
       select @m_intrecordnumber = @a_intpagesize * @a_intpageno
       select @m_intstartrecord = @a_intpagesize * (@a_intpageno - 1) + 1

       if @a_intdays = 0                      --假如不限定天数
          begin
                /*求符合条件记录数*/
                select "recordcount" = count(*)                         
                       from bbs where layer=1 and forumid = @a_forumid 

               /*输出纪录*/
               /*首先定义可滚动光标*/
               set rowcount @m_intrecordnumber
               declare m_curtemp scroll cursor 
                       for
                          select a.id ,a.title , d.username , a.faceid ,
                                contentsize = datalength(a.content) , 
                                totalchilds = (select sum(totalchilds) 
                                                        from bbs as b 
                                                        where a.rootid = b.rootid) ,
                                lastreplytime = (select max(posttime) 
                                                          from bbs as c 
                                                          where a.rootid = c.rootid)
                                from bbs as a 
                                     join bbsuser as d on a.userid = d.id 
                                where layer=1 and forumid = @a_forumid 
                                order by rootid desc , layer , posttime
               open m_curtemp
               fetch absolute @m_intstartrecord from m_curtemp
               while  @@fetch_status = 0 
                      fetch next from m_curtemp

               set rowcount 0 
               /*清场*/       
               close m_curtemp
               deallocate m_curtemp
          end                      
                          
       else                                --假如限定天数          

          begin
                /*求符合条件记录数*/
                select "recordcount" = count(*)                         
                       from bbs where layer=1 and forumid = @a_forumid 
                                      and dateadd(day , @a_intdays , posttime) > getdate() 

               /*输出纪录*/
               /*首先定义可滚动光标*/
               set rowcount @m_intrecordnumber
               declare m_curtemp scroll cursor 
                       for
                          select a.id ,a.title , d.username , a.faceid ,
                                contentsize = datalength(a.content) , 
                                totalchilds = (select sum(totalchilds) 
                                                        from bbs as b 
                                                        where a.rootid = b.rootid) ,
                                lastreplytime = (select max(posttime) 
                                                          from bbs as c 
                                                          where a.rootid = c.rootid)
                                from bbs as a 
                                     join bbsuser as d on a.userid = d.id 
                                where layer=1 and forumid = @a_forumid 
                                      and dateadd(day , @a_intdays , posttime) > getdate() 
                                order by rootid desc , layer , posttime
               open m_curtemp
               fetch absolute @m_intstartrecord from m_curtemp
               while  @@fetch_status = 0 
                      fetch next from m_curtemp

               set rowcount 0 
               /*清场*/       
               close m_curtemp
               deallocate m_curtemp
          end                                                
go


注:若在asp中调用存储过程的command对象为cm,则set rs=cm.execute,然后用set rs=rs.nextrecordset取下一条记录。
三:测试结果。

看到了这么多的分页方法。那么那种最好呢,最好的分页方法是什么呢?
还是做个测试吧。
测试工具:microsoft web application stress tool 1.1
测试平台:win2000 server 中文版 + iis5.0 + sql server 7.0
数据纪录:8000条  (非相同的股票历史纪录集)
模拟环境:56k model / 2m专线 / 10兆专线 
测试次数:3次


测试结果:服务器游标 >== 存储过程分页 > > ado分页 
(符号 >== :表示基本上相同,但有时稍大, 符号:> > 远远大于 )
看来,前两者差别不大,而且在多纪录集时,服务器端游标比存储过程稍大一些。但都比ado游标分页效率要好的多。

那么什么是最好的分页方法呢,我们理想的分页方法是什么呢?
其实就是:客户端传递一个页码过去,然后服务器端直接通过一次查询就生成所需的一页的记录集,并且以一个纪录集的形式返回给客户端。那么这种放法有没有呢?我能够告诉大家,有,而且经过我的测试,确实是效率最好的一种方法。下次我们就谈谈这种理论上最好的分页方法。。。。。。。。。

喜欢本文,那就收藏到:

    Del.icio.us Google书签 Digg Live Bookmark Technorati Furl Yahoo书签 Facebook 百度搜藏 新浪ViVi 365Key网摘 天极网摘 和讯网摘 博拉网 POCO网摘 添加到饭否 QQ书签 Digbuzz我挖网
相关评论  我也要评论
还没有关于此文章的相关评论!
  • 昵称: (为空则显示guest)
  • 评论分数: ★ ★ ★★★ ★★★★ ★★★★★
  • 评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
  • 导航
    赞助商
    文章类别
    订阅