看了下面的文章,發現作者的測試真的對用asp的朋友很有幫助,不只是asp其它的語言應該也類似。在伺服器上產生動態內容是使用ASP最主要的原因之一,所以我們選擇的第一個測試項目是確定把動態內容傳送到應答流使用什麼方法最好。基本的選擇有兩種(以及它們的一些變化):使用內嵌ASP標記,使用Response.Write語句。
為了測試這些不同的方法,我們建立一個簡單的ASP頁面,頁面先定義一些變數然後把它們插入到表格中。雖然這個頁面很簡單,而且沒有實際用途,但它足以讓我們分離和測試各個問題。
2.1 使用ASP內嵌標記
第一個測試是使用ASP的內嵌標記<%= x %>,其中x是一個變數。這是使用最方便的方法,而且它可以讓頁面的HTML部分變得更容易閱讀和維護。
複製代碼代碼如下:
<% OPTION EXPLICIT
Dim FirstName
Dim LastName
Dim MiddleInitial
Dim Address
Dim City
Dim State
Dim PhoneNumber
Dim FaxNumber
Dim EMail
Dim BirthDate
FirstName = John
MiddleInitial = Q
LastName = Public
Address = 100 Main Street
City = New York
State = NY
PhoneNumber = 1-212-555-1234
FaxNumber = 1-212-555-1234
EMail = [email protected]
BirthDate = 1/1/1950
%>
<HTML>
<HEAD>
<TITLE>Response Test</ TITLE>
</HEAD>
<BODY>
<H1>Response Test</H1>
<TABLE>
<tr><td><b>First Name:</b></td><td><%= FirstName %></td></tr>
<tr><td><b>Middle Initial:</b></td><td><%= MiddleInitial %></td></tr>
<tr><td><b>Last Name:</b></td><td><%= LastName %></td></tr>
<tr><td><b>Address:</b></td><td><%= Address %></td></tr>
<tr><td><b>City:</b></td><td><%= City %></td></tr>
<tr><td><b>State:</b></td><td><%= State %></td></tr>
<tr><td><b>Phone Number:</b></td><td><%= PhoneNumber %></td></tr>
<tr><td><b>Fax Number:</b></td><td><%= FaxNumber %></td></tr>
<tr><td><b>EMail:</b></td><td><%= EMail %></td></tr>
<tr><td><b>Birth Date:</b></td><td><%= BirthDate %></td></tr>
</TABLE>
</BODY>
</HTML>
/app1/response1.asp的完整程式碼
最好記錄= 8.28 毫秒/頁
2.2 使用Response.Write輸出每一行HTML程式碼
許多優秀的文獻指出,應避免使用前面的內嵌標記方法,因為它導致一個稱為上下文切換的操作。這個操作發生在Web伺服器所處理的程式碼類型改變的時候(從純HTML的發送到腳本處理,或反過來),這種切換需要一定的時間。許多程式設計師在了解了這一點之後,他們的第一個反應是將每一行HTML程式碼都用Response.Write函數來輸出:
複製代碼代碼如下:
…
Response.Write(<html>)
Response.Write(<head>)
Response.Write( <title>Response Test</title>)
Response.Write(</head>)
Response.Write(<body>)
Response.Write(<h1>Response Test</h1>)
Response.Write(<table>)
Response.Write(<tr><td><b>First Name:</b></td><td> & FirstName & </td></tr>)
Response.Write(<tr><td><b>Middle Initial:</b></td><td> & MiddleInitial & </td></tr>)
…
/app1/response2.asp片斷
最好記錄= 8.28 毫秒/頁
回應時間= 8.08 毫秒/頁
差額= -0.20 毫秒(減少2.4%)
和內嵌標記版本相比,我們所看到的效能改善非常小,簡直令人驚訝。這或許是因為頁面中多出了許多函數呼叫。不過這種方法還有一個更大的缺點,由於HTML程式碼嵌入了函數內,腳本程式碼變得非常冗長,閱讀和維護都不方便。
2.3 使用封裝函數
Response.Write並不會在文字行的最後加上CRLF(Carriage Return - Line Feed,回車換行),這是使用上面這種方法最令人失望的地方。儘管已經在伺服器端把HTML程式碼作了很好的格式化,但在瀏覽器中看到的仍舊有長長的一行程式碼。不過失望的不僅是這一個問題,人們很快就發現不存在能夠自動加入CRLF的Response.WriteLn函數。一個很自然的反應就是創造Response.Write的封裝函數,在每行的後面加上CRLF:
…
writeCR(<tr><td><b>First Name:</b></td><td> & FirstName & </td></tr>)
…
SUB writeCR(str)
Response.Write(str & vbCRLF)
END SUB
/app1/response4.asp片斷
最好記錄= 8.08 毫秒/頁
回應時間= 10.11 毫秒/頁
差額= +2.03 毫秒(增加25.1%)
結果是性能的大大下降。當然,這主要是因為這種方法使得函數的呼叫次數加倍,它對效能的影響非常明顯。不惜代價應該避免這種用法,CRLF導致每行的末尾多了兩個字節,而這兩個位元組對於瀏覽器顯示頁面是沒有用的。在大多數情況下,瀏覽器端HTML程式碼的格式美觀只是方便了你的競爭者閱讀和理解頁面的設計。
2.4 合併多個Response.Write
如果不考慮最後一次有關封裝函數的測試,下一個合理的步驟應是將所有字串從分開的Response.Write語句合併到一個語句,從而減少函數呼叫次數、提高程式碼運作效率。
複製代碼代碼如下:
Response.Write(<html> & _
<head> & _
<title>Response Test</title> & _
</head> & _
<body> & _
<h1>Response Test</h1> & _
<table> & _
<tr><td><b>First Name:</b></td><td> & FirstName & </td></tr> & _
…
<tr><td><b>Birth Date:</b></td><td> & BirthDate & </td></tr> & _
</table> & _
</body> & _
</html>)
/app1/response3.asp片斷
最好記錄= 8.08 毫秒/頁
回應時間= 7.05 毫秒/頁
差額= -1.03 毫秒(減少12.7%)
這是目前為止最好的方法。
2.5 合併多個Response.Write,並在每一行的末端增加CRLF
也有人非常關注他們的HTML程式碼在瀏覽器端是否美觀,因此我們又在每一行HTML程式碼的結尾加上了一個回車,使用的是vbCRLF常數,其他的測試程式碼與上例一樣。
…
Response.Write(<html> & vbCRLF & _
<head> & vbCRLF & _
<title>Response Test</title> & vbCRLF & _
</head> & vbCRLF & _
…
/app1/response5.asp片斷
最好記錄= 7.05 毫秒/頁
回應時間= 7.63 毫秒/頁
差額= +0.58 毫秒(增加8.5%)
結果是效能略有下降,這可能是因為增加了字串連接操作,同時輸出的文字也增加了。
2.6 意見
根據上述ASP輸出測試的結果,我們得到以下編碼規則:
避免過度使用內嵌ASP。
把盡可能多的Response.Write語句合併成單一語句。
絕對不要為了加上CRLF而封裝Response.Write。
如果要格式化HTML輸出,直接在Response.Write語句後面加上CRLF。
綱要:ASP動態產生的內容以什麼方式輸出效率最高?最好用哪一種方法提取資料庫記錄集?本文測試了近20個這類ASP開發中常見的問題,測試工具所顯示的時間告訴我們:這些通常可以想當然的問題不僅值得關注,而且還有出乎意料的秘密隱藏在內。
一、測試目的
本文的第一部分檢視了ASP開發中的一些基本問題,並給出了一些效能測試結果以幫助讀者理解放入頁面的程式碼到底對效能有什麼影響。 ADO是由Microsoft開發的一個通用、易用的資料庫接口,事實證明透過ADO與資料庫互動是ASP最重要的應用之一,在第二部分中,我們就來研究這個問題。
ADO所提供的功能相當廣泛,因此準備本文最大的困難在於如何界定問題的範圍。考慮到提取大量的資料可能顯著增加Web伺服器的負載,所以我們決定這一部分的主要目的是找出什麼是操作ADO記錄集的最優配置。然而,即使縮小了問題的範圍,我們仍面臨很大的困難,因為ADO可以有許多不同的方法來完成同一個任務。例如,記錄集不僅可以透過Recordset類別提取,而且也可以透過Connection和Command類別提取;即使在記錄集物件之後,還有許多可能戲劇性地影響效能的操作方法。然而,與第一部分一樣,我們將盡可能涵蓋最廣泛的問題。
具體地講,這一部分的目標是收集足夠的信息,回答下列問題:
l是否應該透過包含引用ADOVBS.inc?
l使用記錄集時是否應該建立單獨的連接物件?
l最好用哪一種方法提取記錄集?
l哪一種遊標類型和記錄鎖定方式效率最高?
l是否應該使用本機記錄集?
l設定記錄集屬性用哪一種方法最好?
l用哪一種方法引用記錄集欄位值效率最高?
l用臨時字串收集輸出是個好方法嗎?
二、測試環境
本測試總共用到了21個ASP文件,這些文件可以從本文後面下載。每一個頁面設定成可以執行三種不同的查詢,分別回傳0、25、250個記錄。這將有助於我們隔離頁面本身的初始化、運行開銷與用循環存取記錄集的開銷。
為方便測試,資料庫連接字串和SQL命令串都在Global.asa中作為Application變數保存。由於我們的測試資料庫是SQL Server 7.0,因此連線串指定OLEDB作為連線提供者,測試資料來自SQL Server的Northwind資料庫。 SQL SELECT指令從NorthWind Orders表中提取7個指定的欄位。
複製代碼代碼如下:
<SCRIPT LANGUAGE=VBScript RUNAT=Server>
Sub Application_OnStart
Application(Conn) = Provider=SQLOLEDB; & _
Server=MyServer; & _
uid=sa; & _
pwd=; & _
DATABASE=northwind
Application(SQL) = SELECTTOP 0OrderID, & _
CustomerID, & _
EmployeeID, & _
OrderDate, & _
RequiredDate, & _
ShippedDate, & _
Freight & _
FROM[Orders]
End Sub
</SCRIPT>
'alternate sql - 25 records
Application(SQL) = SELECTTOP 25OrderID, & _
CustomerID, & _
EmployeeID, & _
OrderDate, & _
RequiredDate, & _
ShippedDate, & _
Freight & _
FROM[Orders]
'alternate sql - 250 records
Application(SQL) = SELECTTOP 250 OrderID, & _
CustomerID, & _
EmployeeID, & _
OrderDate, & _
RequiredDate, & _
ShippedDate, & _
Freight & _
FROM[Orders]
測試伺服器配置如下:450 Mhz Pentium,512 MB RAM,NT Server 4.0 SP5,MDAC 2.1(資料存取元件),以及5.0版本的Microsoft腳本引擎。 SQL Server運行在另外一台具有類似配置的機器上。和第一部分一樣,我們仍舊使用Microsoft Web Application Stress Tool 記錄從第一個頁面請求到從伺服器接收到最後一個位元組的時間(TTLB,Time To Last Byte),時間以毫秒為單位。測試腳本呼叫每個頁面1300次以上,運行時間約20小時,以下顯示的時間是會話的平均TTLB。請記住,和第一部分一樣,我們只關心程式碼的效率,而不是它的可擴展性或伺服器效能。
同時請注意我們啟用了伺服器的緩衝。另外,為了讓所有的文件名字長度相同,有的文件名字中嵌入了一個或多個下劃線。
三、第一次測試
在第一次測試中,我們模擬Microsoft ASP ADO範例中可找到的典型情形擷取一個記錄集。在這個例子(ADO__01.asp)中,我們先開啟一個連接,然後建立一個記錄集物件。當然,這裡的腳本按照本文第一部分所總結的編碼規則作了優化。
複製代碼代碼如下:
<% Option Explicit %>
<!-- #Include file=ADOVBS.INC -->
<%
Dim objConn
Dim objRS
Response.Write( _
<HTML><HEAD> & _
<TITLE>ADO Test</TITLE> & _
</HEAD><BODY> _
)
Set objConn = Server.CreateObject(ADODB.Connection)
objConn.Open Application(Conn)
Set objRS = Server.CreateObject(ADODB.Recordset)
objRS.ActiveConnection = objConn
objRS.CursorType = adOpenForwardOnly
objRS.LockType = adLockReadOnly
objRS.Open Application(SQL)
If objRS.EOF Then
Response.Write(No Records Found)
Else
'write headings
Response.Write( _
<TABLE BORDER=1> & _
<TR> & _
<TH>OrderID</TH> & _
<TH>CustomerID</TH> & _
<TH>EmployeeID</TH> & _
<TH>OrderDate</TH> & _
<TH>RequiredDate</TH> & _
<TH>ShippedDate</TH> & _
<TH>Freight</TH> & _
</TR> _
)
'write data
Do While Not objRS.EOF
Response.Write( _
<TR> & _
<TD> & objRS(OrderID) & </TD> & _
<TD> & objRS(CustomerID) & </TD> & _
<TD> & objRS(EmployeeID) & </TD> & _
<TD> & objRS(OrderDate) & </TD> & _
<TD> & objRS(RequiredDate) & </TD> & _
<TD> & objRS(ShippedDate) & </TD> & _
<TD> & objRS(Freight) & </TD> & _
</TR> _
)
objRS.MoveNext
Loop
Response.Write(</TABLE>)
End If
objRS.Close
objConn.Close
Set objRS = Nothing
Set objConn = Nothing
Response.Write(</BODY></HTML>)
%>
下面是測試結果:
我們來看看各欄數字的意思:
0返回0個記錄的頁面所需要的TTLB(毫秒)。在所有的測試中,該值被視為產生頁面本身(包括建立物件)的時間開銷,不包含循環存取記錄集資料的時間。
25以毫秒計的擷取並顯示25個記錄的TTLB
tot time/2525欄的TTLB除以25,它是每個記錄的總計平均時間開銷。
disp time/2525欄的TTLB減去0欄的TTLB,然後除以25。該值反映了在循環記錄集時顯示單一記錄所需時間。
250提取和顯示250個記錄的TTLB。
tot time/250250欄的TTLB除以25,此值代表單一記錄的總計平均時間開銷。
disp time/250250欄的TTLB減去0欄的TTLB,再除以250。該值反映了在循環記錄集時顯示單一記錄所需時間。
上面的測試結果將用來與下一個測試結果進行比較。
四、是否應該通過包含引用ADOVBS.inc?
Microsoft提供的ADOVBS.inc包含了270行程式碼,這些程式碼定義了大多數的ADO屬性常數。我們這個範例只從ADOVBS.inc引用了2個常數。因此本次測試(ADO__02.asp)中我們刪除了包含檔案引用,設定屬性時直接使用對應的數值。
objRS.CursorType = 0?' adOpenForwardOnly
objRS.LockType = 1' adLockReadOnly
可以看到頁面開銷下降了23%。該值不會影響單一記錄的擷取和顯示時間,因為這裡的變更不會影響迴圈內的記錄集操作。有多種方法可以解決ADOVBS.inc的引用問題。我們建議將ADOVBS.inc檔作為參考,設定時透過註解加以說明。請記住,如第一部分所指出的,適度地運用註解對程式碼的效率影響極小。另外一種方法是將那些需要使用的常數從ADOVBS.inc檔拷貝到頁面內。
還有一個解決問題的好方法,這就是透過連結ADO類型庫使得所有的ADO常數直接可用。把下面的程式碼加入Global.asa文件,即可直接存取所有的ADO常數:
<!--METADATA TYPE=typelib
FILE=C:Program FilesCommon FilesSYSTEMADOmsado15.dll
NAME=ADODB Type Library -->
或者:
<!--METADATA TYPE=typelib
UUID=00000205-0000-0010-8000-00AA006D2EA4
NAME=ADODB Type Library -->
因此,我們的第一條規則為:
l避免包含ADOVBS.inc文件,透過其他方法存取和使用ADO常數。
五、使用記錄集時是否應該建立單獨的連接物件?
要正確地回答這個問題,我們必須分析兩種不同條件下的測試:第一,頁面只有一個資料庫事務;第二,頁面有多個資料庫事務。
在前例中,我們建立了一個單獨的Connection物件並將它賦給Recordset的ActiveConnection屬性。然而,如ADO__03.asp所示,我們也可以直接把連接串賦給ActiveConnection屬性,在腳本中初始化和配置Connection物件這一額外的步驟可以省略。
objRS.ActiveConnection = Application(Conn)
雖然Recordset物件仍舊要建立一個連接,但此時的建立是在高度最佳化的條件下進行的。因此,與上一次測試相比,頁面開銷又下降了23%,而且如預期的一樣,單一記錄的顯示時間沒有實質的變化。
因此,我們的第二個規則如下:
l如果只使用一個記錄集,直接把連接串賦給ActiveConnection屬性。
接下來我們檢查頁面用到多個記錄集時,上述規則是否仍有效。為測試這種情形,我們引入一個FOR循環將前例重複10次。在這個測試中,我們將研究三種變化:
第一,如ADO__04.asp所示,在每個循環中建立和拆除Connection物件:
複製代碼代碼如下:
Dim i
For i = 1 to 10
Set objConn = Server.CreateObject(ADODB.Connection)
objConn.Open Application(Conn)
Set objRS = Server.CreateObject(ADODB.Recordset)
objRS.ActiveConnection = objConn
objRS.CursorType = 0 'adOpenForwardOnly
objRS.LockType = 1 'adLockReadOnly
objRS.Open Application(SQL)
If objRS.EOF Then
Response.Write(No Records Found)
Else
'write headings
…
'write data
…
End If
objRS.Close
Set objRS = Nothing
objConn.Close
Set objConn = Nothing
Next
第二,如ADO__05.asp所示,在循環外建立Connection對象,所有記錄集共用該對象:
複製代碼代碼如下:
Set objConn = Server.CreateObject(ADODB.Connection)
objConn.Open Application(Conn)
Dim i
For i = 1 to 10
Set objRS = Server.CreateObject(ADODB.Recordset)
objRS.ActiveConnection = objConn
objRS.CursorType = 0 'adOpenForwardOnly
objRS.LockType = 1 'adLockReadOnly
objRS.Open Application(SQL)
If objRS.EOF Then
Response.Write(No Records Found)
Else
'write headings
…
'write data
…
End If
objRS.Close
Set objRS = Nothing
Next
objConn.Close
Set objConn = Nothing
第三,如ADO__06.asp所示,在每個迴圈內把連接串賦給ActiveConnection屬性:
複製代碼代碼如下:
Dim i
For i = 1 to 10
Set objRS = Server.CreateObject(ADODB.Recordset)
objRS.ActiveConnection = Application(Conn)
objRS.CursorType = 0 'adOpenForwardOnly
objRS.LockType = 1 'adLockReadOnly
objRS.Open Application(SQL)
If objRS.EOF Then
Response.Write(No Records Found)
Else
'write headings
…
'write data
…
End If
objRS.Close
Set objRS = Nothing
Next
就像我們猜想的一樣,在循環內創建和拆除連接物件是效率最差的方法。不過,令人驚異的是,在循環內直接把連接串賦給ActiveConnection屬性只比共用單一連線物件稍微慢了一點。
儘管如此,第三規則應該為:
l同一頁面內用到多個記錄集時,建立單一的連接物件並透過ActiveConnection屬性共用它。
六、哪種遊標類型和記錄鎖定方式效率最高?
迄今為止的所有測試中我們只使用了只能向前的遊標來存取記錄集。 ADO為記錄集提供的遊標還有三種類型:靜態可捲動的遊標,動態可捲動的遊標,鍵集遊標。每種遊標都提供不同的功能,例如存取前一記錄和後一記錄、是否可以看到其他程式對資料的修改等。不過,具體討論每一種遊標類型的功用已經超出了本文的範圍,下表是各種遊標類型的一個比較性的分析。
和只能向前類型的遊標相比,所有其它的遊標類型都需要額外的開銷,而且這些遊標在循環內一般也要慢一些。因此,我們願與您共享如下告誡:永遠不要這樣認為-唔,有時候我會用到動態遊標,那麼我就一直使用這種遊標吧。
同樣的看法也適用於記錄鎖定方式的選擇。前面的測試只用到了唯讀的加鎖方式,但還有其他三種方式:保守式、開放式、開放式批次處理方式。和遊標類型一樣,這些鎖定方式為處理記錄集資料提供了不同的功能和控制能力。
我們得出以下規則:
l使用適合處理任務的最簡單的遊標類型和記錄鎖定方式。
七、最好用哪一種方法提取記錄集?
到目前為止我們一直透過建立Recordset物件提取記錄集,但是ADO也提供了間接的記錄集擷取方法。下面的測試比較ADO__03.asp和直接從Connection物件建立記錄集(CONN_01.asp)這兩種方法:
複製代碼代碼如下:
Set objConn = Server.CreateObject(ADODB.Connection)
objConn.Open Application(Conn)
Set objRS = objConn.Execute(Application(SQL))
可以看到頁面開銷略有增加,單一記錄的顯示時間沒有變化。
下面我們再來看看從Command物件直接建立記錄集物件(CMD__02.asp):
複製代碼代碼如下:
Set objCmd = Server.CreateObject(ADODB.Command)
objCmd.ActiveConnection = Application(Conn)
objCmd.CommandText = Application(SQL)
Set objRS = objCmd.Execute
同樣,頁面開銷也略有增加,而單一記錄的顯示時間沒有本質的變化。後面這兩種方法在效能上的差異很小,但我們還有一個重要的問題要考慮。
透過Recordset類別建立記錄集時,我們能夠以最大的靈活性控制記錄集的處理方式。既然後面兩種方法未能有壓倒性的效能表現,我們主要還是考慮預設回傳的遊標類型和記錄鎖定方式,對於某些場合來說預設值並不一定是最理想的。
因此,除非因為特殊的原因需要選擇後面兩種方法,否則我們建議考慮下面的規則:
l透過ADODB.Recordset類別實例化記錄集,以獲得最佳的效能和靈活性。
八、是否應該使用本地記錄集?
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個規則如下:
l除非確實要求記錄集本地化,否則應避免使用。
十、用哪一種方法引用記錄集欄位值效率最高?
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