最近,因在天眼搜索网(http://www.tianyan.com)工作时需要对1300万企业的数据库信息进行分页显示。考虑到数据库非常庞大,如用传统的分页程序必然导致SQL查询时间过长、内存耗费严重、网页打不开的现象。我对网上流行的分页方法进行了详细的对比,期望找到一种完善的解决方案。但是都不是很理想,因此只好亲力亲为编写分页代码,经调试成功后,13000万级别的查询毫秒内完成,上下翻页同样快速,不敢藏私特公布程序代码。
网上流行的分页方式比较 网上流行的分页算法很多,但是整体来看就3种分页方式。我们先把3种分页方式做一个比较。
传统分页程序 设计原理:采用先把所有内容读入内存,然后计算出数据总数,根据每页数据大小通过,rs.pagesize进行分页。每一页都需要对数据进行重复的读入,再根据页号调整rs数据指针进行数据显示。 设计优点: 1、最为简单和最为广泛接受的分页方式。 2、可以根据多重变量进行排序 例如:order by topinfo,infotype,info 设计缺点: 1、每次返页都需要把大量数据读入内存,占用内存过大,资源利用率不高。 2、每次翻页的速度都如同第一次翻页速度一样缓慢。 设计结论: 因其设计原理简单,如是小型access数据库,表数据容量在几千条且不重点考虑网页的处理速度的话是可以考虑采用的。
ID预读分页程序 设计原理:打开第一页的时候先把所有的数据ID号存储在一个数组或者变量中,然后每次分页显示的时候,只需要采用 select * form id in()进行读取。 设计优点: 1、 第一次页面显示速度很慢,以后每页读取速度非常快。 2、 除第一页外,上下页读取、显示速度一样快。 3、 可以根据多重变量进行排序 例如:order by topinfo,infotype,info 设计缺点: 1、 因第一次需要读取所有数据id,所以处理速度比较慢。 2、 读取得id信息需要保存在session()或者cookies中,如用户浏览器禁止cookies存取则分页程序不能正常运作。
设计结论: 程序相对复杂,原理明了,处理十万万条数据左右时可以考虑采用,百万级数据可能会造成页面显示超时现象。如一次对10万条数据进行存储和分页,但是访问者仅访问了前几页信息,则会造成服务器资源的极大浪费。
ID排序式分页程序 设计原理:按照ID的顺序进行页面显示,每次读取页面都采用id< or id>的方式,不需要预读所有数据信息。 设计优点: 1、 因每次仅需要读取指定数量数据,所以访问速度快。 2、 不会造成资源的浪费,根据访问者浏览的页数进行数据调用。 设计缺点: 1、 因采用id排序,所以不能指定其他的排序方式。 2、 下一页的显示速度远远高于上一页的显示速度,访问页数越多越明显。 设计结论: 因每次访问上一页时都需要重新计算id范围,因此向上访问速度慢,且不能根据其他变量进行排序。程序设计复杂,不利于初学者学习、调试。
天眼搜索分页方式 设计原理:综合了ID预读分页程序,ID排序式分页程序两种分页程序的设计思想。重新设计了一种页面存储式分页方式,适合于百万、千万级别的数据显示,显示速度快,资源耗费少。而且采用子程序结构,分页代码设计简单。 设计优点: 1、 资源耗费少,显示速度快。 2、 采用隐藏表单,不需要cookies支持。 3、 读取数据后可迅速释放资源,清理垃圾信息。 设计缺点: 1、 因采用id排序,所以不能指定其他的排序方式。 2、 只有返回首页、上页、下页,无法直接显示尾页 3、 不允许跨越向下访问,例如从第1页直接访问10页信息。 设计结论:支持千万级别的数量显示和读取,显示快速,无环境限制。但是因采用逐步存储方式,不允许跨越访问。及不能按照指定方式排序。
程序假设前提条件 1、conn数据库连接字符串已经连接 2、SQL数据库拥有company表 3、company表包含id,name,address,telephone,cmemo字段 4、id列已经被设定为自动增加,且设置为主键
% 本程序经验证测试通过% seachcode.asp <% '---天眼搜索分页程序 '---http://www.tianyan.com '---开发者:oicq:5124597 '---天眼全国最大的企业信息搜索平台--- '------------------------ '函数名称:cutesql(strsql,strwhere,strsort,MaxPerPage,currentpage,pagecode,pageclass,showtotal) '函数功能:对数据库进行分页 '函数参数:strsql sql字符串 strwhere sql的where字符串 strsort sql的order字符串 ' MaxPerPage 每页最多显示的数据量 currentpage 当前页 pagecode=分页存储信息 pageclass=页面类型 ' showtotal true=显示总数等信息 flase=不显示信息 '返回参数:返回rs数据集,采用rs.getrows()方式 '-------------------------- function cutesql(strsql,strwhere,strsort,MaxPerPage,currentpage,pagecode,pageclass,showtotal) ' response.write "strsql="&strsql&"<br>" ' response.write "strwhere="&strwhere&"<br>" ' response.write "strsort="&strsort&"<br>" ' response.write "currentpage="¤tpage&"<br>" ' response.write "MaxPerPage="&MaxPerPage&"<br>" ' response.write "pagecode="&pagecode&"<br>" ' response.write "pageclass="&pageclass&"<br>" ' response.write "showtotal="&showtotal&"<br>" '-------判断是否向后跳跃翻页--------------------- dim strtmp if currentpage>2 and pageclass="nextpage" then if len(pagecode)=0 or isnull(pagecode) then response.write"<SCRIPT language=javascript>alert('分页信息为空,返回第一页');</SCRIPT>" currentpage=1 elseif instr(pagecode,"p"&cstr(currentpage-1)&":")=0 then response.write"<SCRIPT language=javascript>alert('不允许跳跃向后翻页,返回最后一页');</SCRIPT>" currentpage=cint(mid(pagecode,instr(pagecode,":")+1,instr(pagecode,"||")-1)) end if end if
'-----计算总数、页数等信息------------------ if showtotal then if currentpage=1 then dim tmpsql tmpsql=right(strsql,len(strsql)-instr(strsql,"from")+1) tmpsql="select count(1) "&tmpsql if strwhere<>"" then tmpsql=tmpsql&" where "&strwhere end if 'response.write "tmpsql="&tmpsql totalput=conn.execute(tmpsql)(0) pagecode=cstr(totalput)&":"&cstr(currentpage)&"||" else totalput=cint(left(pagecode,instr(pagecode,":")-1)) end if else if currentpage=1 then pagecode="0:1||" end if totalput=0 end if
'response.write "totalput="&totalput&"<br>" '---------重定义strsql----------------- strsql="select top "&MaxPerPage&" "&strsql if currentpage=1 then if strwhere<>"" then strsql=strsql&" where "&strwhere end if else 'response.write "strsql1="&strsql&"<br>" dim pagearea if pageclass="uppage" then pagearea=right(pagecode,len(pagecode)-instr(pagecode,"p"&cstr(currentpage)&":")+1) pagearea=left(pagearea,instr(pagearea,"||")-1) strsql=strsql&" where id>="&split(pagearea,":")(2)&" and id<="&split(pagearea,":")(1)&" " if strwhere<>"" then strsql=strsql&" and "&strwhere end if elseif pageclass="nextpage" then pagearea=right(pagecode,len(pagecode)-instr(pagecode,"p"&cstr(currentpage-1)&":")+1) pagearea=left(pagearea,instr(pagearea,"||")-1) strsql=strsql&" where id<"&split(pagearea,":")(2)&" " if strwhere<>"" then strsql=strsql&" and "&strwhere end if else response.write "调用cutepage参数错误!" response.end exit function end if end if strsql=strsql&" "&strsort 'response.write "strsql="&strsql&"<br>" '----读取rs数据记录--------- 'response.write "strsql="&strsql&"<br>" 'response.write "conn="&IsObject(Conn)&"<br>" dim rssql On Error Resume Next set rssql=Server.CreateObject("ADODB.Recordset") rssql.open strsql,conn,1,1 if rssql.eof and rssql.bof then if showtal then strtmp="当前没有纪录!" else response.write"<SCRIPT language=javascript>alert('已经到达最后一页或者没有数据,返回上页!');</SCRIPT>" end if else strtmp=rssql.getrows() 'response.write "pagecode2="&pagecode&"<br>" if instr(pagecode,"p"&cstr(currentpage)&":")=0 then pagecode=pagecode & "p"&cstr(currentpage)&":"&strtmp(0,0)&":"&strtmp(0,ubound(strtmp,2))&"||" else pagecode=left(pagecode,instr(pagecode,"p"&cstr(currentpage)&":")-1)& "p"&cstr(currentpage)&":"&strtmp(0,0)&":"&strtmp(0,ubound(strtmp,2))&"||" end if end if rssql.close set rssql=nothing 'response.write "pagecode="&pagecode&"<br>" cutesql=strtmp end function
'************************************************** '过程名:showpage(strUrl,totalput,MaxPerPage,ShowTotal,strUnit,pagecode,currentpage) '作 用:显示“上一页 下一页”等信息 '参 数:strUrl ----链接地址 ' totalput ----总数量 ' maxperpage ----每页数量 ' ShowTotal ----是否显示总数量 ' strUnit ----计数单位 ' pagecode ------分页代码集 ' currentpage -----当前页 '************************************************** sub showpage(strUrl,totalput,MaxPerPage,ShowTotal,strUnit,pagecode,currentpage) dim n, i,strTemp
if ShowTotal then if totalput mod MaxPerPage=0 then n= totalput \ MaxPerPage else n= totalput \ MaxPerPage+1 end if end if strTemp= "<table align='center'><tr><td>" strUrl=replace(strUrl,"&&","&") if right(strurl,1)="&" then strurl=left(strurl,len(strurl)-1) end if 'response.write "strurl="&right(strurl,len(strurl)-instr(strurl,"?"))&"<br>" '-------生成提交表单的隐藏字段开始------------ dim strform,strfile if instr(strurl,"?")>0 then strfile=left(strurl,instr(strurl,"?")-1) strTemp=strTemp & "<form action='"&strfile&"' name='showpageform' method='post'>" if ShowTotal then strTemp=strTemp & "共 <font color=blue><b>" & totalput & "</b></font> " & strUnit & " " end if if instr(strurl,"&")=0 then strform=split(right(strurl,len(strurl)-instr(strurl,"?")),"=") strTemp=strTemp & "<input type='hidden' name='"&strform(0)&"' value='"&strform(1)&"'>" else strform=split(right(strurl,len(strurl)-instr(strurl,"?")),"&") for i=0 to ubound(strform) strTemp=strTemp & "<input type='hidden' name='"&split(strform(i),"=")(0)&"' value='"&split(strform(i),"=")(1)&"'>" next end if else strfile=strurl strTemp=strTemp & "<form action='"&strfile&"' name='showpageform' method='post'>" if ShowTotal then strTemp=strTemp & "共 <font color=blue><b>" & totalput & "</b></font> " & strUnit & " " end if end if strTemp=strTemp & "<input type='hidden' name='pagecode' value='"&pagecode&"'>" strTemp=strTemp & "<input type='hidden' name='page' value='"¤tpage&"'>" strTemp=strTemp & "<input type='hidden' name='pageclass' value='"&pageclass&"'>" '-------结束---------------------------------- if CurrentPage<2 then strTemp=strTemp & "返回首页 " strTemp=strTemp & "最上一页 " else strTemp=strTemp & "<a href='#' oNCLICK='javascript:document.showpageform.pageclass.value="nextpage";document.showpageform.page.value=1;document.showpageform.submit()'>返回首页</a> " strTemp=strTemp & "<a href='#' oNCLICK='javascript:document.showpageform.pageclass.value="uppage";document.showpageform.page.value="&CurrentPage-1&";document.showpageform.submit()'>上一页</a> " end if strTemp=strTemp & "< " if Currentpage>10 then for i=Currentpage-9 to Currentpage strTemp=strTemp & "<a href='#' oNCLICK='javascript:document.showpageform.pageclass.value="uppage";document.showpageform.page.value="&i&";document.showpageform.submit()'>"&i&"</a> " next else for i=1 to Currentpage strTemp=strTemp & "<a href='#' oNCLICK='javascript:document.showpageform.pageclass.value="uppage";document.showpageform.page.value="&i&";document.showpageform.submit()'>"&i&"</a> " next end if strTemp=strTemp & ">" if n-currentpage<1 and showtotal then strTemp=strTemp & "最下一页" else strTemp=strTemp & "<a href='#' oNCLICK='javascript:document.showpageform.pageclass.value="nextpage";document.showpageform.page.value="&CurrentPage+1&";document.showpageform.submit()'>下一页</a> " end if if ShowTotal then strTemp=strTemp & " 页次:<strong><font color=red>" & CurrentPage & "</font>/" & n & "</strong>页 " strTemp=strTemp & " <b>" & MaxPerPage & "</b>" & strUnit & "/页" end if strTemp=strTemp & "</td></form></tr></table>" response.write strTemp end sub %> search.asp 搜索页面 <!--#include file="conn.asp"--> <!--#include file="searchcode.asp"--> <% '-----读取返回的分页信息----------------- dim MaxPerPage,totalPut,CurrentPage,pagecode,pageclass MaxPerPage=5 pagecode=request("pagecode") pageclass=request("pageclass") totalput=request("totalput") if pageclass="" then pageclass="nextpage" end if CurrentPage=trim(request("page")) if CurrentPage="" then CurrentPage=1 else CurrentPage=Clng(CurrentPage) end if '-----读取搜索表单返回的变量------------- dim action,companyname action=request("action") companyname=request("companyname") 'response.write action&companyname if action="search" then call companylist() else call main() end if sub main() %> <form action="search.asp" name="search" method='post'> <input type='hidden' name="action" value="search"> <input type="text" name="companyname" value="天眼"> <input type="submit" value="查询" name="B1"> <form> <% end sub
sub companylist() dim i,strsql,strwhere,strsort,strtmp,infolist '--------设定sql字符串参数--------------- '假设你希望查询的sql语句为"select id,name,address,telephone,cmemo from company where name='"&companyname&"' order by id desc" '请按照下面方式分割sql语句 strsql="id,name,address,telephone,cmemo from company " strwhere=" name like '%"&companyname&"%' " strsort=" order by id desc" '--------进行分页显示---------- infolist=cutesql(strsql,strwhere,strsort,MaxPerPage,currentpage,pagecode,pageclass,true) '--------判断是否查找到数据------ if not isarray(infolist) then response.write"没有查到你要搜索的数据" else for i=0 to ubound(infolist,2) response.write "id="&infolist(0,i)&" name="&infolist(1,i)&" address="&infolist(2,i)&" telephone="&infolist(3,i)&" cmemo="&infolist(4,i)&"<br>" next call showpage("search.asp?action=search&companyname="&companyname,totalput,MaxPerPage,true,"公司",pagecode,currentpage) end if end sub
call closeconn() %>
注释: 1、 可下载源代码测试,地址http://www.tianyan.com/temp/search.rar 2、 数据表采用id desc排序方式,如果你想用id asc排序,可适当修改代码。 3、 本程序需要有一定的asp代码阅读能力 4、 本程序可以任意转砸,但是不要取出程序出处注释信息 |