五、StoredProcedure
在讨论StoredProcedure之前,我还要对Command对象的Execute方法的作用进行一下阐述,一般来说使用Command的Execute方法有三个目的。1、用于进行一些简单的处理,例如删除一条记录:
comm.CommandType=AdCmdText
comm.CommandText="Delect From employee Where Job_ID=1"
comm.execute
这样的工作不需要返回什么东西。2、用于进行一些复杂的处理,例如进行一个Transact的设计,这类一般都是和StoredProcedure一同工作的,而且有输出参数和输入的参数,这也是我们本章的讨论主题。3、用于返回一个RecordSet对象,用于其它的处理,例如:
comm.CommandText="Delect From employee Where Job_ID=1"
set rs=comm.execute
dim i
while not rs.EOF
for i=0 to rs.fileds.count-1
response.write rs.fileds.item(i).value&","
next
response.write "$#@60;br$#@62;"
rs.MoveNext
wend
StoredProcedure的标准写法:(在SQL Server上用Query Analyzer执行)
Create Procedure Procedure_Name
Define Parameter
As
SQL Structure
上 面的语法结构中,Procedure_Name为存储结构的的名字,也是你将在Command中引用的名字。然后是定义输出和输入的参数。最后是一个SQL结构化语句。下面是一个StoredProcedure的例子,它无需输入的参数,也没有输出。
Create Procedure Del_User
As
Delect From Employee Where Job_ID=1
如果我们要删除指定的 Job_ID该怎么办呢?,这时我们需要给这个StoredProcedure输入的参数。
Create Procedure Del_User1
@intID int
As
Delect From Employee Where Job_Id = @intID
好了,这里的@intJob就是一个输入的参数,它可以从外部接受输入的值,下面是给它输入的asp程序:
set comm=Server.CreateObject("ADODB.Command")
conn.ConnectionString="Driver={SQL Server};Server=ser;"& _
"uid=sa;pass=;database=employee "
conn.open
comm.ActiveConnection=conn
comm.CommandType=adCmdStoredProc
comm.CommandType="Del_User1"
"下面就是参数的输入
param=comm.CreateParameter("ID",adInt,adParamInput,4)
"这里的adParamInput定义是最重要的。
Param.Value=1 "这里的值可以输入你想要的值,也可以用Request来获得
Comm.Parameters.Append param
Comm.Execute
这样我们就可以向StoredProcedure传递参数了。有时在一个StoredProcedure中,还存在有输出的参数,下面是一个例子它返回一个Job_ID确定的Fri_Name的值
Create Procedure Get_fName
@intID int
@fName varChar Output "说明为输出的参数
As
Select @fName = Fri_Name Where Job_ID = @intID
它相应的asp程序也要改写为下面的形式
set comm=Server.CreateObject("ADODB.Command")
conn.ConnectionString="Driver={SQL Server};Server=ser;"&_
"uid=sa;psss=;database=employee"
conn.open
comm.ActiveConnection=conn
comm.CommandType=adCmdStoredProc
comm.CommandType="Get_fName"
"下面就是参数的输入
param=comm.CreateParameter("ID",adInt,adParamInput,4)
"这里的adParamInput定义是最重要的。
Param.Value=2 "这里的值可以输入你想要的值,也可以用Request来获得
Comm.Parameters.Append param
param=comm.CreateParameter("fName",adVarchar,adParamOutput,255,"")
"这里的adParamOutput定义是最重要的。说明它是一个输出的参数,默认的值 为一空的字符串
comm.Parameters.Append param
Comm.Execute
Response.Write "Job_Id为"¶m(0)&"的员工的首姓为"¶m(1)
我给大家简单介绍了一下StoredProcedure的基本概念,但StoredProcedure比较复杂,如果你想进一步的深入,必须对SQL Server的结构体系有全面的了解。另外,我们并没有在上面的里子中体会到StoredProcedure的优势,很多人会认为那还不如用普通的方法,其实在构建很多企业级的应用时才能够体会到用StoredProcedure的强大和必要性,这里我举一个简单的例子。一个网络银行的数据库(onLoan)中有两个相关的表Loan表和LoanHistory表,loan表用于记录贷款的信息,而每一笔贷款的记录在Loan表中登记后都必须在LoanHistory表中登记,因为定期的结算都是使用LoanHistory表的。你也许会说那很好办啊。用两个Insert Into语句分别向两个表中插入记录不就行了吗!但要注意的是在这个应用中,若记录在任何的一个表中插入失败都必须将整个的过程给取消(也就是一个事务的取消),那么若仅简单的使用两个Insert Into语句的话,若是在第一个语句执行完毕后,在第二个语句尚未完成时就发生了故障,这时第一个语句产生的效果是没法消除的了。如果我们将这整个的过程定义为一个事务,事务没有完整的结束就Roll Back所有的影响不就达到了要求吗?这在SQL Server中可以用Begin Transaction和Commit Transaction来完成的,例子如下:
As
Begin transaction
Inset into Loan (Loan_ID,Loan_Data,Loan_amount)
Values(?,?,?)
Inset into Loan (Loan_ID,Loan_Data,Loan_amount,Loan_Describle)
Values(?,?,?,?)
Commit Transaction
下面我们来看最后的一个对象─RecordSet对象,也是属性和方法最多的一个了。我们使用的频率也是最高的一个,在这之后,我还想谈谈ADO与ORACLE的一些问题。
六、RecordSet对象
写到这一篇的时候,我不禁想到了先贤的两句话,一句是孟子在曹刿论战中所说的:一鼓作气,再而衰,三而竭。这篇ADO总结报告的前五部分都是一鼓作气之作,不知这后面的再而衰部分是否能保持连续了。另外的一句是王安石在游褒禅山记中所说的:世之奇伟、瑰怪、非常之观,常在于险远,而人之所罕至焉,故非有志者不能至也。我们学习编程又何尝不是这样了,若非有志,能及于险远是不能真正掌握的。
好了,讲了这么多的题外话,还是让我们回到正题上来,我想,由于大家对RecordSet都有足够的认识,所以我就不象前面的几章中那样对它的每一个属性和方法都做完整的介绍,我想通过几个有代表性的编程实例来给大家讲一讲。
1、 方法
AddNew, CancelBatch, CancelUpdate, Clone, Close, Delete, GetRows, Move, MoveFirst, MoveLast, MoveNext, MovePrevious, NextRecordset, Open, Requery, Resync, Supports, Update, UpdateBatch
2、 属性
AbsolutePage,AbsolutePosition,ActiveConnection,BOF,Bookmark, CacheSize, CursorLocation, CursorType, EditMode, EOF, Filter, LockType, MarshalOptions, MaxRecords, PageCount, PageSize, RecordCount, Source, State, Status
3、 集合
Fields,Properties
实例一:分页显示及导航:
为什么我要再提分页的这个问题呢?因为这是一个最基本的问题,虽然有很多关于分页的文章,但我觉得他们的方法偏于复杂。其实RecordSet的AbsolutePage就可以轻松的实现分页,当你指定了PageSize属性后,对AbsolutePage指定值就可以翻转到指定的页面。但是如果你想使用AbsolutePage的话,你必须在打开RecordSet对象之前将它的CursorLocation值设为adUseClient,这个属性是继承Connection对象的一个相同属性的。你也可以在打开Connection对象之前来设定它。下面是源代码,为了方便,我将导航栏独立成了一个子程序方便大家使用。
sub navigator(PageNo,Target)
Response.Write "$#@60;table border=0$#@62;"
Response.Write "$#@60;tr$#@62;"
Response.Write "$#@60;td$#@62;"
if PageNo$#@62;1 then
Response.write "$#@60;a href="&chr(34)&Target&"?Page=1"&chr(34)&"$#@62;┃第一页$#@60;/a$#@62;"
else
Response.Write "┃第一页"
end if
Response.Write "$#@60;/td$#@62;"
Response.Write "$#@60;td$#@62;"
if PageNo$#@60;rs.PageCount then
Response.write"$#@60;a href="&chr(34)&Target&"?Page="&PageNo+1&chr(34)&"$#@62;┃下一页$#@60;/a$#@62;"
else
Response.Write "┃下一页"
end if
Response.Write "$#@60;/td$#@62;"
Response.Write "$#@60;td$#@62;"
if PageNo$#@62;1 then
Response.write "$#@60;a href="&chr(34)&Target&"?Page="&PageNo-1&chr(34)&"$#@62;┃前一页$#@60;/a$#@62;"
else
Response.Write "┃前一页"
end if
Response.Write "$#@60;/td$#@62;"
Response.Write "$#@60;td$#@62;"
if PageNo$#@60;rs.PageCount then
Response.write "$#@60;a href="&chr(34)&Target&"?Page="&rs.PageCount&chr(34)&"$#@62;┃最后一页$#@60;/a$#@62;"
else
Response.Write "┃最后一页"
end if
Response.Write "$#@60;/td$#@62;"
Response.Write "$#@60;td$#@62;"
Response.Write "┃页次:"&PageNo&"/"&rs.PageCount&"页┃"&rs.PageSize&"条记录/页┃"
Response.Write "$#@60;/td$#@62;"
Response.Write "$#@60;td valign="middle"$#@62;"
Response.Write "$#@60;form action="&chr(34)&Target&chr(34)&" method="&chr(34)&"POST"&chr(34)&"$#@62;"
Response.Write "$#@60;input type="text"size=3 maxlength=4 name="Page"$#@62;"
Response.Write " $#@60;input type="submit"value="转到"$#@62;"
Response.Write "$#@60;/form$#@62;"
Response.Write "$#@60;/td$#@62;"
Response.Write "$#@60;/tr$#@62;"
Response.Write "$#@60;/table$#@62;"
end sub
%$#@62;
const adCmdText=&H0001
const adVarChar=200
const adInteger=3
const adParamInput=&H0001
const adCmdTable=&H0002
const adUseClient=3
const adDate=7
const adLongVarChar=201
set conn=Server.CreateObject("ADODB.Connection")
conn.ConnectionString="Driver={Microsoft Access Driver (*.mdb)};DBQ="& _
Server.Mappath("/source_asp")&"/process/process.mdb;"
conn.Open
%$#@62;
const MaxPageSize=5
%$#@62;
$#@60;html$#@62;
$#@60;head$#@62;
$#@60;title$#@62; See Book $#@60;/title$#@62;
$#@60;/head$#@62;
$#@60;body$#@62;
$#@60;%
dim i,j,PageNo
set rs=Server.CreateObject("ADODB.RecordSet")
rs.ActiveConnection=conn
rs.CursorLocation=adUseClient
rs.Open "Select * From books",,,adCmdText
if rs.BOF then
Response.Write "欢迎使用图书,资料管理程序!"
else
rs.PageSize=MaxPageSize
if isempty(Request.QueryString("Page")) then
PageNo=1
elseif cInt(Request.QueryString("Page"))$#@60;1 then
PageNo=1
elseif cInt(Request.QueryString("Page"))$#@62;rs.PageCount then
PageNo=rs.PageCount
else
PageNo=cInt(Request.QueryString("Page"))
end if
if Request.ServerVariables("Request_Method")="POST" and not Isempty(Request.Form("Page")) then
PageNo=cInt(Request.Form("Page"))
end if
rs.AbsolutePage=PageNo
Response.Write "$#@60;table border="0" width="100%"$#@62;"
Response.Write "$#@60;tr$#@62;$#@60;td colspan="&rs.fields.count&"$#@62;"
Target="books.asp"
call navigator(PageNo,Target) "调用导航栏
Response.Write "$#@60;/td$#@62;$#@60;/tr$#@62;"
Response.Write "$#@60;tr$#@62;"
for i=0 to rs.fields.count-1
Response.Write "$#@60;td$#@62;"&rs.fields.item(i).name&"$#@60;/td$#@62;"
next
Response.Write "$#@60;/tr$#@62;"
j=0
while (not rs.EOF) and j$#@60;rs.PageSize
Response.Write "$#@60;tr$#@62;"
for i=0 to rs.fields.count-1
if i=1 then
Response.Write "$#@60;td$#@62;"&"$#@60;a href="&chr(34)&"status.asp?BookName="& _
rs.fields.item(i).value&chr(34)&"$#@62;"&rs.fields.item(i).value&"$#@60;/a$#@62;$#@60;/td$#@62;"
"这里这样写是为了级联式查询而做的。
else
Response.Write "$#@60;td$#@62;"&rs.fields.item(i).value&"$#@60;/td$#@62;"
end if
next
Response.Write "$#@60;/tr$#@62;"
rs.MoveNext
j=j+1
wend
Response.Write "$#@60;/table$#@62;"
end if
%$#@62;
