通过 ASP 记录进行分页

发表于:2007-06-30来源:作者:点击数: 标签:
简介 在 Active Server Pages (ASP) 应用程序中显示大型记录集,很可能是您熟悉的一个问题。本文对此问题及其解决方案和示例代码进行了深入探讨,这个示例代码经过简单修改,就可以应用于您的具体情况。该示例代码被设计成服务器端的解决方案,它与浏览器无关
简介

在 Active Server Pages (ASP) 应用程序中显示大型记录集,很可能是您熟悉的一个问题。本文对此问题及其解决方案和示例代码进行了深入探讨,这个示例代码经过简单修改,就可以应用于您的具体情况。该示例代码被设计成服务器端的解决方案,它与浏览器无关。另外,我会指出您在设计自己的解决方案时需要考虑的问题。

问题

您的查询返回了一个大型记录集。需要提供一个简便方法来浏览这些结果,即在每页上只显示结果的子集。要有效的完成此项工作,需要对 ActiveX(R) 数据对象 (ADO) 和数据库如何协同工作有深入的了解。

解决方案

如何将您的记录集分成“页”,而不用大型的结果?所谓页,基本上就是您指定应当显示在一起的许多记录。例如,如果您的记录集中有 100 条记录,可能每页显示 10 条记录。

ADO 提供了两种方法,PageSize 和 AbsolutePage。这些方法使您能够指定每页要显示的记录数,以及将游标定位于一页的开始。

打开记录集之后,基本步骤就是:

为该记录集指定 PageSize。它表示每页要显示的记录数。
指定该记录集的 AbsolutePage。这将记录指针移到页的序列中,给定页的开始处。
显示记录页。要完成这一步,您要用设置的 PageSize 次数循环整个记录集,或者直到到达文件的末尾。
示例代码

下列示例代码说明了页面建立过程。借助它,您可以建立自己的解决方案的原型。在您自己的代码中,确保要完成下列步骤:

添加错误处理。
添加对查询返回的记录数的限制。
用条件过滤记录。(如,建立 WHERE 子句)。
使用存储过程或视图。
一定要通过更改连接字符串和 SQL 语句来修改我的示例代码,以指向您的数据库。由于代码使用 ADO 常数,如 adUserServer,一定要在您的 Global.asa 文件中引用 ADO TypeLibrary,或在 ASP 页中包括 ADOVBS.INC 文件。请注意,在将项目引用设置为 Microsoft ADO 时,Visual InterDev(R) 会为您自动生成 TypeLibrary 引用。

注意该示例有两种方法可以提供导航栏:

ShowNavBar。 它为用户提供了带着记录计数一起跳到指定页的方法(见图 1)。为实现这一步,它使用了 RecordCount 和 PageCount 属性。
ShowNavBarFast。 该方法不提供跳转到指定页的能力,也不提供记录计数,但可以通过 CacheSize 属性控制取回的记录数(见图 2)。
PageThroughRs.Asp

<%@ Language=VBScript %>
<% Option Explicit %>
<SCRIPT LANGUAGE=VBScript RUNAT=SERVER>
@#确保引用 ADO Typelib 或使用 ADOVBS.Inc
Dim iPageNum, iRowsPerPage

Main
Sub Main()
Dim rst
Dim sSQL, sConnString

If Request.QueryString("iPageNum") = "" Then
iPageNum = 1
Else
iPageNum = Request.QueryString("iPageNum")
iPageNum = CInt(iPageNum)
End If

iRowsPerPage = 10


sConnString = "Provider=SQLOLEDB.1;password=Xyz123;user id=WebUser;" & _
"Initial Catalog=NorthWind;Data Source=MySQLServer;" & _
"network=dbmssocn;"

@#下列 SQL 从 SQL 视图中检索所有列。
@#要优化性能
@#- 使用存储过程、视图或在 SELECT 中指定列
@#- 使用限制返回的记录的条件(例如,WHERE 子句)
sSQL = "SELECT CategoryName, ProductName, QuantityPerUnit,"
sSQL = sSQL & "UnitsInStock, Discontinued"
sSQL = sSQL & " FROM [Products By Category]"

Set rst = GetRecords(sConnString, sSQL)

WriteTableHeader rst
WriteTableBody rst, iRowsPerPage, iPageNum
ShowNavBar rst

@#ShowFastNavBar 方法不使用 RecordCount
@#或 PageCount,所以它重试的记录数仅等于
@#记录集的 CacheSize 指定的数量。

@#ShowFastNavBar rst

CleanUp rst
End Sub

Function GetRecords(sConnString, sSQL)
Dim cnn
Dim rst

set cnn = Server.CreateObject("ADODB.CONNECTION")
cnn.ConnectionString = sConnString
nn.Open

Set rst = Server.CreateObject("ADODB.RECORDSET")

Set rst.ActiveConnection = cnn

@#当记录集打开时,adUseClient 的 CursorLocation
@# 将检索所有的记录。
@#adUseServer 允许沿用 CacheSize
rst.CursorLocation = adUseServer

@#在使用服务器端游标时,CacheSize
@#限制了取回的行数。我们将只抓取正在显示的
@#的记录的数目 - iRowsPerPage
rst.CacheSize = iRowsPerPage

rst.Open sSQL,,adOpenStatic, adLockReadOnly牋?
Set GetRecords = rst
end Function

Sub WriteTableHeader(rst)
Dim fld

Response.Write "<TABLE WIDTH=80% BORDER=1>"
Response.Write "<TR>"

@#建立表的列标题
For Each fld in rst.Fields
Response.Write "<TD><B>" & fld.Name & "</B></TD>"
Next
Response.Write "</TR>"
End Sub

Sub WriteTableBody(rst, iRowsPerPage, iPageNum)
Dim iLoop
Dim fld

iLoop = 1

rst.PageSize = iRowsPerPage
rst.AbsolutePage = iPageNum

@#写出记录的当前页
Do While (Not rst.EOF) and (iLoop <= iRowsPerPage)
Response.Write "<TR>"
For Each fld in rst.Fields
Response.Write "<TD>" & fld.value & "</TD>"
Next
iLoop = iLoop + 1
rst.MoveNext
Response.Write "</TR>"
Loop
Response.Write "</TABLE>"
End Sub

Sub ShowNavBar(rst)
Dim iPageCount
Dim iLoop
Dim sScriptName

@#本版本提供了更丰富的用户导航,但是
@#依赖于 RecordCount 和 PageCount,
@#它抵消了为服务器端游标
@#指定 CacheSize 的好处。

Response.Write "<BR><BR>"
sScriptName = Request.ServerVariables("SCRIPT_NAME")

If iPageNum > 1 Then
Response.Write " <a href=" & sScriptName & "?iPageNum="
Response.Write (iPageNum -1) & "><< Previous</a>"
End If

iPageCount = rst.PageCount
Do Until iLoop > iPageCount
f iLoop = iPageNum Then
Response.Write " <B>" & CStr(iLoop) & "</B>"
Else
Response.Write " <a href=" & sScriptName & "?iPageNum=" & _
Cstr(iLoop) & ">" & iLoop & "</a>"
End If
iLoop = iLoop + 1
Loop

If Not rst.EOF Then
Response.Write " <a href=" & sScriptName & "?iPageNum="
Response.Write (iPageNum +1) & "> Next >></a><BR>"
Else
Response.Write "<BR>"
End If

Response.Write "Page " & iPageNum & " of " & iPageCount & "<BR>"
Response.Write rst.RecordCount & " Records" 牋?
End Sub

Sub ShowFastNavBar(rst)
Dim iPageCount
Dim iLoop
Dim sScriptName

@#在指定 CacheSize 和使用服务器端游标时,
@#该方法特别有效,因为它不使用 RecordCount
@#和 PageCount。需要用户具有经验。

Response.Write "<BR><BR>"
sScriptName = Request.ServerVariables("SCRIPT_NAME")

If iPageNum > 1 Then
Response.Write " <a href=" & sScriptName & "?iPageNum="
Response.Write (iPageNum -1) & "><< Previous</a>"
End If

If Not rst.EOF Then
Response.Write " <a href=" & sScriptName & "?iPageNum="
Response.Write (iPageNum +1) & "> Next >></a><BR>"
Else
Response.Write "<BR>"
End If

Response.Write "Page " & iPageNum

End Sub

Sub CleanUp(rst)
If Not rst Is Nothing then
If rst.state = adStateOpen then rst.close
set rst = nothing
End If
End Sub

</SCRIPT>
分析

设计分页解决方案时,需注意的几个问题:

游标定位问题。如果使用客户端游标,每次打开记录集时,将读取所有的记录。因此,由于读取了所有的记录,以后访问 RecordCount 或 PageCount 属性时将很快。如果您使用服务器端游标,将只检索需要的记录。您可以通过 CacheSize 属性指定一次要读取的记录数来提高性能。然而,如果您使用服务器端游标,和 RecordCount 或 PageCount 属性,则将读取所有的记录,性能得不到提高。必须在具有更多信息和更丰富导航的用户界面,与检索所有记录的性能影响之间折衷。
使用服务器端游标时,CursorType 属性必须是 adOpenStatic 或 adOpenKeyset,才能使用分页。
分页并非总是最好的用户页面。它可能仅适用于用户正从搜索引擎扫描结果或浏览产品目录的情况。
试将记录分类,以使更相关的记录出现在前几页中(例如,使用 SQL 的 ORDER BY 子句)。用户所能做的就这么多。
只检索需要显示的列(即,避免 SELECT *)。
只检索需要显示的记录。确保过滤的条件(即,使用 WHERE 子句)。
以下是需要牢记的几点提示:

将您的逻辑封装在方法中。使用方法可将表示逻辑和数据访问逻辑分离,这就简化了将代码装入 Windows 脚本组件、Visual Basic 脚本编辑 (VBScript) 类或组件的工作。改变功能更容易了,代码维护也得以改进。测试和调试也因可以注释和取消注释方法调用而得到改进。
与包括 ADOVBS.INC 相比,引用 ADO 的 TypeLibrary 是更好的解决方案。这是因为 ASP 在处理包含文件时,是将整个文件读入内存,而不是只读入它需要的部分。
结论

分页是一项通用技术,许多 Web 应用程序用它来提供浏览大量记录的好方法。在设计分页解决方案时,需要考虑一些问题,如,如何检索记录,需要提供什么类型的用户导航。尽管最好的解决方案取决于您的具体的应用程序,使用本文中的技术将帮助您作出更好的设计决策。

原文转自:http://www.ltesting.net