直播中
-- 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
該過(guò)程首先檢查是否有同名的職員存在,并據(jù)此設(shè)定相應(yīng)的變量Exists,若存在同名,就設(shè)為1,否則為0。然后將該職員加到表中,同時(shí)把Exists的值作為返回值返回。
注意盡管返回了一個(gè)值,但并未將其聲明為存儲(chǔ)過(guò)程的參數(shù)。
調(diào)用該過(guò)程的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
%>
需要重點(diǎn)注意,返回值應(yīng)當(dāng)作為集合中第一個(gè)參數(shù)被創(chuàng)建。即使返回值并不作為一個(gè)參數(shù)出現(xiàn)在存儲(chǔ)過(guò)程中,總是Parameters集合中的第一個(gè)Parameters。
因此,特別強(qiáng)調(diào)一點(diǎn):
存儲(chǔ)過(guò)程的返回值必須聲明為Parameters集合中第一個(gè)參數(shù),同時(shí)參數(shù)的Direction值必須為adParamReturnValue。
使用返回值
現(xiàn)在定義一個(gè)初始窗體,如圖9-3所示:
圖9-3 初始窗體界面
按下Add Employee按鈕會(huì)產(chǎn)生如圖9-4所示的顯示:
圖9-4 按下Add Employee按鈕后顯示的界面
再添加同樣的細(xì)節(jié)(ID號(hào)不同)會(huì)得到如圖9-5所示的界面:
圖9-5 添加細(xì)節(jié)后顯示的界面
6. 更新參數(shù)
無(wú)需輸入所有的參數(shù)細(xì)節(jié),只需調(diào)用Refresh方法,就能讓ADO完成更新。例如,假設(shè)已經(jīng)創(chuàng)建了一個(gè)帶有與前面例子相同的參數(shù)的過(guò)程usp_AddEmployee,并且沒有改變運(yùn)行的頁(yè)面。
With cmdEmployee
.ActiveConnection = strConn
.CommandText = "usp_Addemployee"
.CommandType = adCmdStoredProc
然后調(diào)用Refresh方法。
.Parameters.Refresh
這告訴ADO向數(shù)據(jù)存儲(chǔ)請(qǐng)求每個(gè)參數(shù)的細(xì)節(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")
注意并不需要?jiǎng)?chuàng)建任何參數(shù),包括返回值。
這似乎真是一條捷徑,但應(yīng)意識(shí)到這種方法也造成了性能上的損失,因?yàn)锳DO必須向提供者查詢以獲得存儲(chǔ)過(guò)程的參數(shù)細(xì)節(jié)。盡管如此,這種方法還是很有用的,尤其是在從參數(shù)中取出正確的值有困難的時(shí)候。
實(shí)際上,可以編寫一個(gè)小實(shí)用程序作為開發(fā)工具使用,用來(lái)完成更新并建立Append語(yǔ)句,可以將其粘貼到自己的代碼中。它看上去應(yīng)該與圖9-6所示的GenerateParameters.asp ASP頁(yè)面類似。
圖9-6 GenerateParameters.asp ASP頁(yè)面
其代碼相當(dāng)簡(jiǎn)單。首先是包含連接符串和另一個(gè)ADOX常數(shù)文件。
<!-- #INCLUDE FILE="../Include/Connection.asp" -->
<!-- #INCLUDE FILE="../Include/ADOX.asp" -->
接下來(lái)創(chuàng)建一個(gè)窗體,指定目標(biāo)為PrintParameters.asp ASP頁(yè)面。
<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中得到存儲(chǔ)過(guò)程的列表,同時(shí)創(chuàng)建一個(gè)含有這些存儲(chǔ)過(guò)程名字的列表框。
<%
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>
這是一個(gè)簡(jiǎn)單的窗體,包括一個(gè)用于顯示連接字符串的TEXTAREA控件和用于顯示存儲(chǔ)過(guò)程名稱的SELECT控件。以前沒有見過(guò)的是ADOX,ADOX是數(shù)據(jù)定義與安全的ADO擴(kuò)展,可以用來(lái)訪問(wèn)數(shù)據(jù)存儲(chǔ)的目錄(或是元數(shù)據(jù))。
本書不打算介紹ADOX的內(nèi)容,但其十分簡(jiǎn)單。進(jìn)一步的細(xì)節(jié)可參見《ADO Programmer's Reference》,Wrox出版社出版,2.1版或2.5版都行。
上面的例子使用了Procedures集合,這個(gè)集合包含數(shù)據(jù)存儲(chǔ)中的所有存儲(chǔ)過(guò)程的列表。按下PrintParameters按鈕時(shí),將得到圖9-7所示的顯示:
圖9-7 按下Print Parameters按鈕時(shí)顯示的界面
可以簡(jiǎn)單地從這里拷貝參數(shù)行到代碼中。在前面使用了一個(gè)以前從未見過(guò)的包含文件。該文件包含了幾個(gè)將ADO常數(shù)(例如數(shù)據(jù)類型、參數(shù)方向等)轉(zhuǎn)換為字符串值的函數(shù):
<!-- #INCLUDE FILE="../Include/Descriptions.asp" -->
接下來(lái),定義一些變量,提取用戶請(qǐng)求并創(chuàng)建Command對(duì)象。
<%
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方法自動(dòng)填寫Parameters集合。
.Parameters.Refresh
現(xiàn)在可以遍歷整個(gè)集合,寫出包含創(chuàng)建參數(shù)所需的細(xì)節(jié)內(nèi)容的字符串。
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站點(diǎn)http://www.wrox.com中找到。
這是一個(gè)非常簡(jiǎn)單的技術(shù),它較好地使用了Refresh方法。
9.3 優(yōu)化
優(yōu)化是每個(gè)開發(fā)人員應(yīng)該關(guān)心的問(wèn)題。對(duì)于數(shù)據(jù)庫(kù)訪問(wèn),優(yōu)化是一個(gè)關(guān)鍵問(wèn)題。和其他任務(wù)相比,數(shù)據(jù)的訪問(wèn)顯得相對(duì)慢些。
因?yàn)閿?shù)據(jù)訪問(wèn)的變化是如此之多,以致于幾乎不可能提出一套固定的數(shù)據(jù)庫(kù)操作的優(yōu)化規(guī)則。通常碰到這類問(wèn)題,經(jīng)常得到這樣的回答:“這取決于……”,因?yàn)檫@類優(yōu)化問(wèn)題取決于準(zhǔn)備做什么。
9.3.1 常用的ADO技巧
盡管優(yōu)化取決于所執(zhí)行的任務(wù),但是仍然有一些常用的技巧:
· 僅選擇所需的列。當(dāng)打開記錄集時(shí),不要自動(dòng)地使用表名(即SELECT *),除非需要獲得所有的列。使用單獨(dú)的列意味著將減少發(fā)送到服務(wù)器或從服務(wù)器取出的數(shù)據(jù)的數(shù)量。即使需要使用全部列,單獨(dú)地命名每個(gè)列也會(huì)獲得最佳的性能,因?yàn)榉?wù)器不必再解釋這些列是什么名字。
· 盡可能使用存儲(chǔ)過(guò)程。存儲(chǔ)過(guò)程是預(yù)先編譯的程序,含有一個(gè)已經(jīng)準(zhǔn)備好的執(zhí)行計(jì)劃,所以比SQL語(yǔ)句執(zhí)行得更快。
· 使用存儲(chǔ)過(guò)程更改數(shù)據(jù)。這總是比在記錄集上使用ADO方法執(zhí)行速度快。
· 除非必需否則不要?jiǎng)?chuàng)建記錄集。運(yùn)行操作查詢時(shí),要確定加入了adExecuteNoRecords選項(xiàng),這樣記錄集就不會(huì)創(chuàng)建。當(dāng)僅僅返回一個(gè)或兩個(gè)字段的單行記錄時(shí)(比如ID值),也可以在查詢狀態(tài)下使用這種方法。在這種情況下,存儲(chǔ)過(guò)程和輸出參數(shù)將會(huì)更快。
· 使用適當(dāng)?shù)墓鈽?biāo)和鎖定模式。如果所做的全部工作是從記錄集中讀取數(shù)據(jù),并將其顯示在屏幕上(比如,創(chuàng)建一個(gè)表),那么使用缺省的只能前移的、只讀的記錄集。ADO用來(lái)維護(hù)記錄和鎖定細(xì)節(jié)的工作越少,執(zhí)行的性能就越高。
9.3.2 對(duì)象變量
當(dāng)遍歷記錄集時(shí),一個(gè)保證能提高性能的方法是使用對(duì)象變量指向集合中的成員。例如,考慮下面的遍歷含有Authors表的記錄集的例子。
While Not rsAuthors.EOF
Response.Write rsAuthors("au_fname") & " " & _
rsAuthors("au_lname") & "<BR>"
rsAuthors.MoveNext
Wend
可以用下面的方法加速代碼執(zhí)行,同時(shí)使其更易于理解。
Set FirstName = rsAuthors("au_fname")
Set LastName = rsAuthors("au_lname")
While Not rsAuthors.EOF
Response.Write FirstName & " " & LastName & "<BR>"
rsAuthors.MoveNext
Wend
這里使用了兩個(gè)變量,并指向記錄集的Fidds集合中的特定字段(記住,F(xiàn)idds集合是缺省的集合)。因?yàn)檫@里建立了一個(gè)對(duì)象的引用,所以可以使用對(duì)象變量而不是實(shí)際的變量,這意味著腳本引擎的工作減少了,因?yàn)樵诩现羞M(jìn)行索引的次數(shù)變少了。
9.3.3 高速緩存大小
高速緩存的大小是指ADO每次從數(shù)據(jù)存儲(chǔ)中讀取的記錄的數(shù)量,缺省為1。這意味著當(dāng)使用基于服務(wù)器的光標(biāo)時(shí),每當(dāng)移動(dòng)到另一條記錄時(shí),必須從數(shù)據(jù)存儲(chǔ)中提取記錄。舉一個(gè)例子,如果增大高速緩存的大小為10,那么每次讀ADO緩沖區(qū)的記錄數(shù)將變?yōu)?0。如果訪問(wèn)位于高速緩存內(nèi)的記錄,那么ADO不需要從數(shù)據(jù)存儲(chǔ)中取記錄。當(dāng)訪問(wèn)位于高速緩存外的記錄時(shí)則下一批記錄將讀入到高速緩存中。
通過(guò)使用記錄集的CacheSize屬性,可以設(shè)置高速緩存的大小。
rsAuthors.CacheSize = 10
可以在記錄集生命期的任何時(shí)候改變高速緩存的大小,但新的數(shù)量只在提取下一批記錄后才有效。
與許多改進(jìn)性能的技巧類似,高速緩存沒有通用的最佳大小,因?yàn)樗S任務(wù)、數(shù)據(jù)和提供者的不同而改變。但是,從1開始增加高速緩存的大小總是能提高性能。
如果你想看到這一點(diǎn),可以使用SQL Server profiler并查看使用缺省的高速緩存打開一個(gè)記錄集發(fā)生的情況,并比較增大高速緩存后發(fā)生的情況。增大高速緩存的大小不僅減低了ADO的工作量,同時(shí)也降低了SQL Server的工作量。
9.3.4 數(shù)據(jù)庫(kù)設(shè)計(jì)
不要希望只通過(guò)編程來(lái)提高對(duì)數(shù)據(jù)的訪問(wèn)效率,應(yīng)該同時(shí)考慮一下數(shù)據(jù)庫(kù)的設(shè)計(jì)。這里并不打算對(duì)數(shù)據(jù)庫(kù)設(shè)計(jì)進(jìn)行更多的討論,但在使用Web站點(diǎn)數(shù)據(jù)庫(kù)時(shí)應(yīng)考慮以下幾點(diǎn):
· 實(shí)時(shí)數(shù)據(jù):向用戶顯示數(shù)據(jù)時(shí),確保數(shù)據(jù)內(nèi)容總是最新是十分重要的。以一份產(chǎn)品目錄為例,目錄內(nèi)容改變的頻率有多快?如果該目錄并非經(jīng)常改變,那么不必每次都從數(shù)據(jù)庫(kù)中提取數(shù)據(jù)。每周一次,或在數(shù)據(jù)改變時(shí)從數(shù)據(jù)庫(kù)產(chǎn)生一個(gè)靜態(tài)的HTML頁(yè)面應(yīng)是一個(gè)更好的辦法。
· 索引:如果需要對(duì)表進(jìn)行大量的查詢,而不執(zhí)行太多的添加數(shù)據(jù)操作,那么可以考慮為表建立索引。
· 不規(guī)范化:如果站點(diǎn)有兩個(gè)不同的目的(數(shù)據(jù)維護(hù)與數(shù)據(jù)分析),那么可以考慮采用一些不規(guī)范化的表以便有助于數(shù)據(jù)的分析??梢蕴峁┆?dú)立的、完全不規(guī)范化的但能正常更新的分析用表,為了改善性能甚至可以將這些分析表移到另一臺(tái)機(jī)器上。
· 數(shù)據(jù)庫(kù)統(tǒng)計(jì):如果使用的是SQL Server 6.x,如果數(shù)據(jù)被添加或刪附除,那么應(yīng)定期更新統(tǒng)計(jì)結(jié)果。這些統(tǒng)計(jì)結(jié)果用于產(chǎn)生一個(gè)查詢計(jì)劃,會(huì)影響查詢的運(yùn)行。請(qǐng)閱讀SQL Books Online中的UPDATE STATISTIC以便了解更詳細(xì)的內(nèi)容。在SQL Server 7.0中這一任務(wù)自動(dòng)完成。 這些都是十分基本的數(shù)據(jù)庫(kù)設(shè)計(jì)技巧,但若只埋頭于ASP代碼可能不會(huì)考慮到這些。