5. 返回值
对函数返回值的处理不同于存储过程返回值的处理,这常常导致混淆。在函数中,经常是返回一个布尔值来表明函数运行的成功与否。
If SomeFunctionName() = True Then
' Function succeeded
但在调用一个存储过程时,却不能使用同样的方法,因为存储是用Execute方法运行的,同时返回一个记录集。
Set rsAuthors = cmdAuthors.Execute
如果得不到一个返回值,如何确定是否已正确执行存储过程?当发生错误时,会报告错误,这样就可使用前一章提供的错误处理代码来处理错误。但对于一些非致命的逻辑错误怎么办?
例如,考虑向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
该过程首先检查是否有同名的职员存在,并据此设定相应的变量Exists,若存在同名,就设为1,否则为0。然后将该职员加到表中,同时把Exists的值作为返回值返回。
注意尽管返回了一个值,但并未将其声明为存储过程的参数。
调用该过程的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.<
>"
If lngAdded = 1 Then
Response.Write "An employee with the same name already exists."
End If
Set cmdEmployee = Nothing
%>
需要重点注意,返回值应当作为集合中第一个参数被创建。即使返回值并不作为一个参数出现在存储过程中,总是Parameters集合中的第一个Parameters。
因此,特别强调一点:
存储过程的返回值必须声明为Parameters集合中第一个参数,同时参数的Direction值必须为adParamReturnValue。
使用返回值
现在定义一个初始窗体,如图9-3所示:
图9-3 初始窗体界面
按下Add Employee按钮会产生如图9-4所示的显示:
图9-4 按下Add Employee按钮后显示的界面
再添加同样的细节(ID号不同)会得到如图9-5所示的界面:
图9-5 添加细节后显示的界面
6. 更新参数
无需输入所有的参数细节,只需调用Refresh方法,就能让ADO完成更新。例如,假设已经创建了一个带有与前面例子相同的参数的过程usp_AddEmployee,并且没有改变运行的页面。
With cmdEmployee
.ActiveConnection = strConn
.CommandText = "usp_Addemployee"
.CommandType = adCmdStoredProc
然后调用Refresh方法。
.Parameters.Refresh
这告诉ADO向数据存储请求每个参数的细节,并创建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")
注意并不需要创建任何参数,包括返回值。
这似乎真是一条捷径,但应意识到这种方法也造成了性能上的损失,因为ADO必须向提供者查询以获得存储过程的参数细节。尽管如此,这种方法还是很有用的,尤其是在从参数中取出正确的值有困难的时候。
实际上,可以编写一个小实用程序作为开发工具使用,用来完成更新并建立Append语句,可以将其粘贴到自己的代码中。它看上去应该与图9-6所示的GenerateParameters.asp ASP页面类似。
图9-6 GenerateParameters.asp ASP页面
其代码相当简单。首先是包含连接符串和另一个ADOX常数文件。
<!-- #INCLUDE FILE="../Include/Connection.asp" -->
<!-- #INCLUDE FILE="../Include/ADOX.asp" -->
接下来创建一个窗体,指定目标为PrintParameters.asp ASP页面。
<FORM NAME="Procedures" METHOD="post" ACTION="PrintParameters.asp">
Connection String:<BR>
<TEXTAREA NAME="txtConnection" COLS="80" ROWS="5">
<% = strConn %>
</TEXTAREA>
<
>
Stored Procedure:<BR>
<SELECT NAME="lstProcedures">
然后,使用ADOX从SQL Server中得到存储过程的列表,同时创建一个含有这些存储过程名字的列表框。
<%
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>
<
>
<INPUT TYPE="submit" VALUE="Print Paramaters">
</FORM>
这是一个简单的窗体,包括一个用于显示连接字符串的TEXTAREA控件和用于显示存储过程名称的SELECT控件。以前没有见过的是ADOX,ADOX是数据定义与安全的ADO扩展,可以用来访问数据存储的目录(或是元数据)。
本书不打算介绍ADOX的内容,但其十分简单。进一步的细节可参见《ADO Programmer's Reference》,Wrox出版社出版,2.1版或2.5版都行。
上面的例子使用了Procedures集合,这个集合包含数据存储中的所有存储过程的列表。按下PrintParameters按钮时,将得到图9-7所示的显示:
图9-7 按下Print Parameters按钮时显示的界面
可以简单地从这里拷贝参数行到代码中。在前面使用了一个以前从未见过的包含文件。该文件包含了几个将ADO常数(例如数据类型、参数方向等)转换为字符串值的函数:
<!-- #INCLUDE FILE="../Include/Descriptions.asp" -->
接下来,定义一些变量,提取用户请求并创建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><
><
>"
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
现在可以遍历整个集合,写出包含创建参数所需的细节内容的字符串。
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包含文件中可以找到函数DataTypeDesc和ParamDirectionDesc。
Descriptions.asp包含文件以及其他的例子文件可以在Web站点http://www.wrox.com中找到。
这是一个非常简单的技术,它较好地使用了Refresh方法。
9.3 优化
优化是每个开发人员应该关心的问题。对于数据库访问,优化是一个关键问题。和其他任务相比,数据的访问显得相对慢些。
因为数据访问的变化是如此之多,以致于几乎不可能提出一套固定的数据库操作的优化规则。通常碰到这类问题,经常得到这样的回答:“这取决于……”,因为这类优化问题取决于准备做什么。
9.3.1 常用的ADO技巧
尽管优化取决于所执行的任务,但是仍然有一些常用的技巧:








