八、是否应该使用本地记录集?
ADO允许使用本地(客户端)记录集,此时查询将提取记录集内的所有数据,查询完成后连接可以立即关闭,以后使用本地的游标访问数据,这为释放连接带来了方便。使用本地记录集对于访问那些要求数据离线使用的远程数据服务非常重要,那么,对于普通的应用它是否同样有所帮助?
下面我们加入CursorLocation属性,并在打开记录集之后关闭了连接(CLIENT1.asp):
Set objRS = Server.CreateObject("ADODB.Recordset")
objRS.CursorLocation = 2' adUseClient
objRS.ActiveConnection = Application("Conn")
objRS.LockType = 1?' adLockReadOnly
objRS.Open Application("SQL")
objRS.ActiveConnection = Nothing
理论上,这种方法由于以下两个原因会对效率有所好处:第一,它避免了在记录之间移动时重复地通过连接请求数据;第二,由于能够方便地释放连接,它减轻了资源需求。然而,从上表看起来使用本地记录集对提高效率显然没有什么帮助。这或许是因为使用本地记录集时,不管程序设置的是什么,游标总是变成静态类型。
第6个规则如下:
·除非确实要求记录集本地化,否则应避免使用。
十、用哪种方法引用记录集字段值效率最高?
10.1 测试
至此为止我们一直通过名字引用记录集中的字段值。由于这种方法要求每次都必须寻找相应的字段,它的效率并不高。为证明这一点,下面这个测试中我们通过字段在集合中的索引引用它的值(ADO__08.asp):
'write data
Do While Not objRS.EOF
Response.Write( _
"<TR>" & _
"<TD>" & objRS(0) & "</TD>" & _
"<TD>" & objRS(1) & "</TD>" & _
"<TD>" & objRS(2) & "</TD>" & _
"<TD>" & objRS(3) & "</TD>" & _
"<TD>" & objRS(4) & "</TD>" & _
"<TD>" & objRS(5) & "</TD>" & _
"<TD>" & objRS(6) & "</TD>" & _
"</TR> " _
)
objRS.MoveNext
Loop
和预期的一样,页面开销也有小小的变化(这或许是因为代码略有减少)。然而,这种方法在显示时间上的改善是相当明显的。
在下一个测试中,我们把所有的字段分别绑定到变量(ADO__09.asp):
If objRS.EOF Then
Response.Write("No Records Found")
Else
'write headings
...
Dim fld0
Dim fld1
Dim fld2
Dim fld3
Dim fld4
Dim fld5
Dim fld6
Set fld0 = objRS(0)
Set fld1 = objRS(1)
Set fld2 = objRS(2)
Set fld3 = objRS(3)
Set fld4 = objRS(4)
Set fld5 = objRS(5)
Set fld6 = objRS(6)
'write data
Do While Not objRS.EOF
Response.Write( _
"<TR>" & _
"<TD>" & fld0 & "</TD>" & _
"<TD>" & fld1 & "</TD>" & _
"<TD>" & fld2 & "</TD>" & _
"<TD>" & fld3 & "</TD>" & _
"<TD>" & fld4 & "</TD>" & _
"<TD>" & fld5 & "</TD>" & _
"<TD>" & fld6 & "</TD>" & _
"</TR>" _
)
objRS.MoveNext
Loop
Set fld0 = Nothing
Set fld1 = Nothing
Set fld2 = Nothing
Set fld3 = Nothing
Set fld4 = Nothing
Set fld5 = Nothing
Set fld6 = Nothing
Response.Write("</TABLE>")
End If
这是目前为止最好的记录。请注意单个记录的显示时间已经降低到0.45毫秒以下。
上述脚本都要求对结果记录集的构造有所了解。例如,我们在列标题中直接使用了字段名字,单独地引用各个字段值。下面这个测试中,不仅字段数据通过遍历字段集合得到,而且字段标题也用同样的方式得到,这是一种更为动态的方案(ADO__10.asp)。
If objRS.EOF Then
Response.Write("No Records Found")
Else
'write headings Response.Write("<TABLE BORDER=1><TR>")
For Each objFld in objRS.Fields
Response.Write("<TH>" & objFld.name & "</TH>")
Next
Response.Write("</TR>")
'write data
Do While Not objRS.EOF
Response.Write("<TR>")
For Each objFld in objRS.Fields
? Response.Write("<TD>" & objFld.value & "</TD>")
Next
Response.Write("</TR>")
objRS.MoveNext
Loop
Response.Write("</TABLE>")
End If
可以看到,代码性能有所下降,但它仍旧要比ADO__07.asp要快。
下一个测试示例是前面两个方法的折衷。我们将继续保持动态特征,同时通过在动态分配的数组中保存字段引用提高性能:
If objRS.EOF Then
Response.Write("No Records Found")
Else
Dim fldCount
fldCount = objRS.Fields.Count
Dim fld()
ReDim fld(fldCount)
Dim i
For i = 0 to fldCount-1
Set fld(i) = objRS(i)
Next
'write headings
Response.Write("<TABLE BORDER=1><TR>") For i = 0 to fldCount-1
Response.Write("<TH>" & fld(i).name & "</TH>")
Next
Response.Write("</TR>")
'write data
Do While Not objRS.EOF
Response.Write("<TR>")
For i = 0 to fldCount-1
Response.Write("<TD>" & fld(i) & "</TD>")
Next
Response.Write("</TR>")
objRS.MoveNext
Loop
For i = 0 to fldCount-1
Set fld(i) = Nothing
Next
Response.Write("</TABLE>")
End If
虽然还不能超过以前最好的成绩,但它比开头的几个示例要快,同时它具有动态地处理任何记录集这一优点。
与前面的测试代码相比,下面的测试代码有了根本性的改动。它使用记录集对象的GetRows方法填充数组以供循环访问数据,而不是直接访问记录集本身。注意在调用GetRows之后立即把Recordset设置成了Nothing,也就是尽快地释放了系统资源。另外,请注意数组的第一维代表字段,第二维代表行(ADO__12.asp)。
If objRS.EOF Then
Response.Write("No Records Found")
objRS.Close
Set objRS = Nothing
Else
'write headings
...
'set array
Dim arrRS
arrRS = objRS.GetRows
'close recordset early
objRS.Close
Set objRS = Nothing
'write data
Dim numRows
Dim numFlds
Dim row
Dim fld
numFlds = Ubound(arrRS, 1)
numRows = Ubound(arrRS, 2)
For row= 0 to numRows
Response.Write("<TR>")
For fld = 0 to numFlds
Response.Write("<TD>" & arrRS(fld, row) & "</TD>")
Next
Response.Write("</TR>")
Next
Response.Write("</TABLE>")
End If
使用GetRows方法时,整个记录集都被提取到了数组。虽然记录集极端庞大时可能产生资源问题,但是用循环访问数据的速度确实更快了,这是由于取消了MoveNext和检查EOF之类的函数调用。
速度是要付出代价的,现在记录集的元数据已经丢失了。为解决这个问题,我们可以在调用GetRows之前从记录集对象提取标题信息;此外,数据类型和其他信息也可以预先提取。另外还要注意的是,测试中性能上的优势只有在记录集较大的时候才会出现。
这一组的最后一个测试中,我们使用了记录集的GetString方法。GetString方法将整个记录集提取成为一个大的字符串,并允许指定分隔符(ADO__13.asp):
If objRS.EOF Then
Response.Write("No Records Found")
objRS.Close
Set objRS = Nothing
Else
'write headings
...
'set array
Dim strTable
strTable = objRS.GetString (2, , "</TD><TD>", "</TD></TR><TR><TD>")
'close recordset early
objRS.Close
Set objRS = Nothing
Response.Write(strTable & "</TD></TR></TABLE>")
End If
虽然这种方法在速度上的好处非常明显,但它只适用于最简单的操作,根本无法适应稍微复杂的数据操作要求。