ASP 3.0高級編程(412)
發(fā)表時間:2023-08-05 來源:明輝站整理相關軟件相關文章人氣:
[摘要]5. 返回值對函數(shù)返回值的處理不同于存儲過程返回值的處理,這常常導致混淆。在函數(shù)中,經常是返回一個布爾值來表明函數(shù)運行的成功與否。If SomeFunctionName() = True Then...
5. 返回值
對函數(shù)返回值的處理不同于存儲過程返回值的處理,這常常導致混淆。在函數(shù)中,經常是返回一個布爾值來表明函數(shù)運行的成功與否。
If SomeFunctionName() = True Then
' Function succeeded
但在調用一個存儲過程時,卻不能使用同樣的方法,因為存儲是用Execute方法運行的,同時返回一個記錄集。
Set rsAuthors = cmdAuthors.Execute
如果得不到一個返回值,如何確定是否已正確執(zhí)行存儲過程?當發(fā)生錯誤時,會報告錯誤,這樣就可使用前一章提供的錯誤處理代碼來處理錯誤。但對于一些非致命的邏輯錯誤怎么辦?
例如,考慮向employee表添加一個新職員的情形。你可能不想防止兩個職員同名的情況,但想注明這個情況。那么,可以使用一個返回值以表明是否已有同名的職員存在。存儲過程如下:
CREATE PROCEDURE usp_AddEmployee
@Emp_ID Char(9),
@FName Varchar(20),
@Minit Char(1),
@LName Varchar(30),
@Job_ID SmallInt,
@Job_Lvl TinyInt,
@Pub_ID Char(4),
@Hire_Date Datetime
AS
BEGIN
DECLARE @Exists Int -- Return value
-- See if an employee with the same name exists
IF EXISTS(SELECT *
FROM Employee
WHERE FName = @FName
AND MInit = @MInit
AND LName = @LName)
SELECT @Exists = 1
ELSE
SELECT @Exists = 0
INSERT INTO Employee (emp_id, fname, minit, lname,
job_id, job_lvl, pub_id, hire_date)
VALUES (@Emp_Id, @FName, @MInit, @LName, @Job_ID,
@Job_Lvl, @Pub_ID, @Hire_Date)
RETURN @Exists
END
該過程首先檢查是否有同名的職員存在,并據(jù)此設定相應的變量Exists,若存在同名,就設為1,否則為0。然后將該職員加到表中,同時把Exists的值作為返回值返回。
注意盡管返回了一個值,但并未將其聲明為存儲過程的參數(shù)。
調用該過程的ASP代碼如下:
<!-- #INCLUDE FILE="../include/Connection.asp" -->
<%
Dim cmdEmployee
Dim lngRecs
Dim lngAdded
Set cmdEmployee = Server.CreateObject("ADODB.Command")
' Set the properties of the command
With cmdEmployee
.ActiveConnection = strConn
.CommandText = "usp_AddEmployee"
.CommandType = adCmdStoredProc
' Create the parameters
' Notice that the return value is the first parameter
.Parameters.Append .CreateParameter ("RETURN_VALUE", adInteger, _
adParamReturnValue)
.Parameters.Append .CreateParameter ("@Emp_id", adChar, adParamInput, 9)
.Parameters.Append .CreateParameter ("@fname", adVarWChar, adParamInput, 20)
.Parameters.Append .CreateParameter ("@minit", adChar, adParamInput, 1)
.Parameters.Append .CreateParameter ("@lname", adVarWChar, adParamInput, 30)
.Parameters.Append .CreateParameter ("@job_id", adSmallInt, adParamInput)
.Parameters.Append .CreateParameter ("@job_lvl", adUnsignedTinyInt, adParamInput)
.Parameters.Append .CreateParameter ("@pub_id", adChar, adParamInput, 4)
.Parameters.Append .CreateParameter ("@hire_date", adDBTimeStamp, _
adParamInput, 8)
' Set the parameter values
.Parameters("@Emp_id") = Request.Form("txtEmpID")
.Parameters("@fname") = Request.Form("txtFirstName")
.Parameters("@minit") = Request.Form("txtInitial")
.Parameters("@lname") = Request.Form("txtLastName")
.Parameters("@job_id") = Request.Form("lstJobs")
.Parameters("@job_lvl") = Request.Form("txtJobLevel")
.Parameters("@pub_id") = Request.Form("lstPublisher")
.Parameters("@hire_date") = Request.Form("txtHireDate")
' Run the stored procedure
.Execute lngRecs, , adExecuteNoRecords
' Extract the return value
lngAdded = .Parameters("RETURN_VALUE")
End With
Response.Write "New employee added.<P>"
If lngAdded = 1 Then
Response.Write "An employee with the same name already exists."
End If
Set cmdEmployee = Nothing
%>
需要重點注意,返回值應當作為集合中第一個參數(shù)被創(chuàng)建。即使返回值并不作為一個參數(shù)出現(xiàn)在存儲過程中,總是Parameters集合中的第一個Parameters。
因此,特別強調一點:
存儲過程的返回值必須聲明為Parameters集合中第一個參數(shù),同時參數(shù)的Direction值必須為adParamReturnValue。
使用返回值
現(xiàn)在定義一個初始窗體,如圖9-3所示:
圖9-3 初始窗體界面
按下Add Employee按鈕會產生如圖9-4所示的顯示:
圖9-4 按下Add Employee按鈕后顯示的界面
再添加同樣的細節(jié)(ID號不同)會得到如圖9-5所示的界面:
圖9-5 添加細節(jié)后顯示的界面
6. 更新參數(shù)
無需輸入所有的參數(shù)細節(jié),只需調用Refresh方法,就能讓ADO完成更新。例如,假設已經創(chuàng)建了一個帶有與前面例子相同的參數(shù)的過程usp_AddEmployee,并且沒有改變運行的頁面。
With cmdEmployee
.ActiveConnection = strConn
.CommandText = "usp_Addemployee"
.CommandType = adCmdStoredProc
然后調用Refresh方法。
.Parameters.Refresh
這告訴ADO向數(shù)據(jù)存儲請求每個參數(shù)的細節(jié),并創(chuàng)建Parameters集合。然后可以為其賦值。
.Parameters("@Emp_Id") = Request.Form("txtEmpID")
.Parameters("@FName") = Request.Form("txtFirstName")
.Parameters("@MInit") = Request.Form("txtInitial")
.Parameters("@LName") = Request.Form("txtLastName")
.Parameters("@Job_ID") = Request.Form("lstJobs")
.Parameters("@Job_Lvl") = Request.Form("txtJobLevel")
.Parameters("@Pub_ID") = Request.Form("lstPublisher")
.Parameters("@Hire_Date") = Request.Form("txtHireDate")
注意并不需要創(chuàng)建任何參數(shù),包括返回值。
這似乎真是一條捷徑,但應意識到這種方法也造成了性能上的損失,因為ADO必須向提供者查詢以獲得存儲過程的參數(shù)細節(jié)。盡管如此,這種方法還是很有用的,尤其是在從參數(shù)中取出正確的值有困難的時候。
實際上,可以編寫一個小實用程序作為開發(fā)工具使用,用來完成更新并建立Append語句,可以將其粘貼到自己的代碼中。它看上去應該與圖9-6所示的GenerateParameters.asp ASP頁面類似。
圖9-6 GenerateParameters.asp ASP頁面
其代碼相當簡單。首先是包含連接符串和另一個ADOX常數(shù)文件。
<!-- #INCLUDE FILE="../Include/Connection.asp" -->
<!-- #INCLUDE FILE="../Include/ADOX.asp" -->
接下來創(chuàng)建一個窗體,指定目標為PrintParameters.asp ASP頁面。
<FORM NAME="Procedures" METHOD="post" ACTION="PrintParameters.asp">
Connection String:<BR>
<TEXTAREA NAME="txtConnection" COLS="80" ROWS="5">
<% = strConn %>
</TEXTAREA>
<P>
Stored Procedure:<BR>
<SELECT NAME="lstProcedures">
然后,使用ADOX從SQL Server中得到存儲過程的列表,同時創(chuàng)建一個含有這些存儲過程名字的列表框。
<%
Dim catPubs
Dim procProcedure
' Predefine the quote character
strQuote = Chr(34)
Set catPubs = Server.CreateObject("ADOX.Catalog")
catPubs.ActiveConnection = strConn
For Each procProcedure In catPubs.Procedures
Response.Write "<OPTION VALUE=" & _
strQuote & procProcedure.Name & _
strQuote & ">" & procProcedure.Name
Next
Set procProcedure = Nothing
Set catPubs = Nothing
%>
</SELECT>
<P>
<INPUT TYPE="submit" VALUE="Print Paramaters">
</FORM>
這是一個簡單的窗體,包括一個用于顯示連接字符串的TEXTAREA控件和用于顯示存儲過程名稱的SELECT控件。以前沒有見過的是ADOX,ADOX是數(shù)據(jù)定義與安全的ADO擴展,可以用來訪問數(shù)據(jù)存儲的目錄(或是元數(shù)據(jù))。
本書不打算介紹ADOX的內容,但其十分簡單。進一步的細節(jié)可參見《ADO Programmer's Reference》,Wrox出版社出版,2.1版或2.5版都行。
上面的例子使用了Procedures集合,這個集合包含數(shù)據(jù)存儲中的所有存儲過程的列表。按下PrintParameters按鈕時,將得到圖9-7所示的顯示:
圖9-7 按下Print Parameters按鈕時顯示的界面
可以簡單地從這里拷貝參數(shù)行到代碼中。在前面使用了一個以前從未見過的包含文件。該文件包含了幾個將ADO常數(shù)(例如數(shù)據(jù)類型、參數(shù)方向等)轉換為字符串值的函數(shù):
<!-- #INCLUDE FILE="../Include/Descriptions.asp" -->
接下來,定義一些變量,提取用戶請求并創(chuàng)建Command對象。
<%
Dim cmdProc
Dim parP
Dim strConnection
Dim strProcedure
Dim strQuote
' Get the connection and procedure name from the user
strQuote = Chr(34)
strConnection = Request.Form("txtConnection")
strProcedure = Request.Form("lstProcedures")
'Update the user
Response.Write "Connecting to <B>" & strConnection & "</B><BR>"
Response.Write "Documenting parameters for <B>" & _
strProcedure & "</B><P><P>"
Set cmdProc = Server.CreateObject("ADODB.Command")
' Set the properties of the command, using the name
' of the procedure that the user selected
With cmdProc
.ActiveConnection = strConnection
.CommandType = adCmdStoredProc
.CommandText = strProcedure
然后使用Refresh方法自動填寫Parameters集合。
.Parameters.Refresh
現(xiàn)在可以遍歷整個集合,寫出包含創(chuàng)建參數(shù)所需的細節(jié)內容的字符串。
For Each parP In .Parameters
Response.Write ".Parameters.Append & _
"("strQuote & parP.Name & _
strQuote & ", " & _
DataTypeDesc(parP.Type) & ", " & _
ParamDirectionDesc(parP.Direction) & _
", " & _
parP.Size & ")<BR>"
Next
End With
Set cmdProc = Nothing
%>
在Descriptions.asp包含文件中可以找到函數(shù)DataTypeDesc和ParamDirectionDesc。
Descriptions.asp包含文件以及其他的例子文件可以在Web站點http://www.wrox.com中找到。
這是一個非常簡單的技術,它較好地使用了Refresh方法。
9.3 優(yōu)化
優(yōu)化是每個開發(fā)人員應該關心的問題。對于數(shù)據(jù)庫訪問,優(yōu)化是一個關鍵問題。和其他任務相比,數(shù)據(jù)的訪問顯得相對慢些。
因為數(shù)據(jù)訪問的變化是如此之多,以致于幾乎不可能提出一套固定的數(shù)據(jù)庫操作的優(yōu)化規(guī)則。通常碰到這類問題,經常得到這樣的回答:“這取決于……”,因為這類優(yōu)化問題取決于準備做什么。
9.3.1 常用的ADO技巧
盡管優(yōu)化取決于所執(zhí)行的任務,但是仍然有一些常用的技巧:
· 僅選擇所需的列。當打開記錄集時,不要自動地使用表名(即SELECT *),除非需要獲得所有的列。使用單獨的列意味著將減少發(fā)送到服務器或從服務器取出的數(shù)據(jù)的數(shù)量。即使需要使用全部列,單獨地命名每個列也會獲得最佳的性能,因為服務器不必再解釋這些列是什么名字。
· 盡可能使用存儲過程。存儲過程是預先編譯的程序,含有一個已經準備好的執(zhí)行計劃,所以比SQL語句執(zhí)行得更快。
· 使用存儲過程更改數(shù)據(jù)。這總是比在記錄集上使用ADO方法執(zhí)行速度快。
· 除非必需否則不要創(chuàng)建記錄集。運行操作查詢時,要確定加入了adExecuteNoRecords選項,這樣記錄集就不會創(chuàng)建。當僅僅返回一個或兩個字段的單行記錄時(比如ID值),也可以在查詢狀態(tài)下使用這種方法。在這種情況下,存儲過程和輸出參數(shù)將會更快。
· 使用適當?shù)墓鈽撕玩i定模式。如果所做的全部工作是從記錄集中讀取數(shù)據(jù),并將其顯示在屏幕上(比如,創(chuàng)建一個表),那么使用缺省的只能前移的、只讀的記錄集。ADO用來維護記錄和鎖定細節(jié)的工作越少,執(zhí)行的性能就越高。
9.3.2 對象變量
當遍歷記錄集時,一個保證能提高性能的方法是使用對象變量指向集合中的成員。例如,考慮下面的遍歷含有Authors表的記錄集的例子。
While Not rsAuthors.EOF
Response.Write rsAuthors("au_fname") & " " & _
rsAuthors("au_lname") & "<BR>"
rsAuthors.MoveNext
Wend
可以用下面的方法加速代碼執(zhí)行,同時使其更易于理解。
Set FirstName = rsAuthors("au_fname")
Set LastName = rsAuthors("au_lname")
While Not rsAuthors.EOF
Response.Write FirstName & " " & LastName & "<BR>"
rsAuthors.MoveNext
Wend
這里使用了兩個變量,并指向記錄集的Fidds集合中的特定字段(記住,F(xiàn)idds集合是缺省的集合)。因為這里建立了一個對象的引用,所以可以使用對象變量而不是實際的變量,這意味著腳本引擎的工作減少了,因為在集合中進行索引的次數(shù)變少了。
9.3.3 高速緩存大小
高速緩存的大小是指ADO每次從數(shù)據(jù)存儲中讀取的記錄的數(shù)量,缺省為1。這意味著當使用基于服務器的光標時,每當移動到另一條記錄時,必須從數(shù)據(jù)存儲中提取記錄。舉一個例子,如果增大高速緩存的大小為10,那么每次讀ADO緩沖區(qū)的記錄數(shù)將變?yōu)?0。如果訪問位于高速緩存內的記錄,那么ADO不需要從數(shù)據(jù)存儲中取記錄。當訪問位于高速緩存外的記錄時則下一批記錄將讀入到高速緩存中。
通過使用記錄集的CacheSize屬性,可以設置高速緩存的大小。
rsAuthors.CacheSize = 10
可以在記錄集生命期的任何時候改變高速緩存的大小,但新的數(shù)量只在提取下一批記錄后才有效。
與許多改進性能的技巧類似,高速緩存沒有通用的最佳大小,因為它隨任務、數(shù)據(jù)和提供者的不同而改變。但是,從1開始增加高速緩存的大小總是能提高性能。
如果你想看到這一點,可以使用SQL Server profiler并查看使用缺省的高速緩存打開一個記錄集發(fā)生的情況,并比較增大高速緩存后發(fā)生的情況。增大高速緩存的大小不僅減低了ADO的工作量,同時也降低了SQL Server的工作量。
9.3.4 數(shù)據(jù)庫設計
不要希望只通過編程來提高對數(shù)據(jù)的訪問效率,應該同時考慮一下數(shù)據(jù)庫的設計。這里并不打算對數(shù)據(jù)庫設計進行更多的討論,但在使用Web站點數(shù)據(jù)庫時應考慮以下幾點:
· 實時數(shù)據(jù):向用戶顯示數(shù)據(jù)時,確保數(shù)據(jù)內容總是最新是十分重要的。以一份產品目錄為例,目錄內容改變的頻率有多快?如果該目錄并非經常改變,那么不必每次都從數(shù)據(jù)庫中提取數(shù)據(jù)。每周一次,或在數(shù)據(jù)改變時從數(shù)據(jù)庫產生一個靜態(tài)的HTML頁面應是一個更好的辦法。
· 索引:如果需要對表進行大量的查詢,而不執(zhí)行太多的添加數(shù)據(jù)操作,那么可以考慮為表建立索引。
· 不規(guī)范化:如果站點有兩個不同的目的(數(shù)據(jù)維護與數(shù)據(jù)分析),那么可以考慮采用一些不規(guī)范化的表以便有助于數(shù)據(jù)的分析?梢蕴峁┆毩⒌摹⑼耆灰(guī)范化的但能正常更新的分析用表,為了改善性能甚至可以將這些分析表移到另一臺機器上。
· 數(shù)據(jù)庫統(tǒng)計:如果使用的是SQL Server 6.x,如果數(shù)據(jù)被添加或刪附除,那么應定期更新統(tǒng)計結果。這些統(tǒng)計結果用于產生一個查詢計劃,會影響查詢的運行。請閱讀SQL Books Online中的UPDATE STATISTIC以便了解更詳細的內容。在SQL Server 7.0中這一任務自動完成。
這些都是十分基本的數(shù)據(jù)庫設計技巧,但若只埋頭于ASP代碼可能不會考慮到這些。