在当今提出的多种动态网页(dhtml)解决方案中都强调了与数据库的连接,其实网页接挂后台数据库也是当前的热门应用,在电子商务等领域有着广泛的应用.microsoft为了适应其发展推出了新的ado(active data object)模型,通过odbc的连接可以对当前流行的桌面数据库系统提供方便,快洁的连接.在这里,我想通过ado在asp技术中的应用来总结一下ado对象以及其各自的属性和方法.
一、ado对象模型:
下面简单介绍一下ado的对象模型
ado有三大对象,即connection对象,command对象,recordset对象.
connection(对象)
─errors(集合)─error(对象)
command(对象)
─parameters(集合)─parameter(对象)
recordset(对象)
─fields(集合)─field(对象)
其中connection对象,command对象,recordset对象和field对象又分别具有properities集合而产生properity对象.我们在asp编程中已经对这几个对象有了足够的了解.下面是以上三大对象的相互关系.
command.activeconnection-$#@62;connection
recordset.activeconnection-$#@62;connection
connection.execute-$#@62;recordset
command.execute-$#@62;recordset
recordset.source-$#@62;command
好了,有了以上的关系表后,你应该能理解有一些等价的表达形式了.
形式1
set rs = server.createobject("adodb.recordset")
strconn = "driver={sql server};server=srv;"& _
"uid=sa;pwd=;database=pubs"
strsql = "select * from employee ;"
rs.open strsql,strconn,,,adcmdtext
形式2
set conn = server.createobject("adodb.connection")
strconn ="driver={sql server};server=srv;"& _
"uid=sa;pwd=;database=pubs"
conn.open strconn
set rs = server.createobject("adodb.recordset")
rs.activeconnection=conn(注意此句)
strsql = "select * from employee ;"
rs.open strsql,,,,adcmdtext
"上面这句也可以写成rs.open strsql,conn,,,adcmdtext
形式3
set conn = server.crreateobject("adodb.connection")
set rs = server.createobject("adodb.recordset")
strconn ="driver={sql server};server=srv;"& _
"uid=sa;pwd=;database=pubs"
conn.open strconn
strsql = "select * from employee ;"
rs=conn.execu strsql(注意次句)
形式4
set rs = server.createobject("adodb.recordset")
strconn ="driver={sql server};server=srv;"& _
"uid=sa;pwd=;database=pubs"
strsql = "select * from employee ;"
rs.open strsql,strconn,,,adcmdtext
注:上面的例子中均假设sql server的name=srv,使用sql server authentication采用了默认的帐号sa,该帐号没有设定密码。
上面这几种形式都能够产生一个相同的recordset对象的实例rs,但方法各异,在后面的讨论中我们将看到它们不同的优越性.
二、connection对象:
在ado的模型中,connection对象是最基本的对象,他主要是提供与数据库的连接。其他的两个对象都是通过它与数据库的连接来完成操作的。它的属性、方法如下所示。
connection对象的主要属性
1、 cursorlocation,它的取值有两个,一个是aduseclient,一个是aduseserver(默认),从其英语本身的含义就可以看出,前者是使用客户端的游标,而后者是使用服务器端的游标。二着的差别在于aduseclient游标可以提供供应商所没有提供的额外的属性,因而灵活性更大。需要注意的是connection对象与recordset对象均有此属性,由connection对象产生的recordset对象会自动的继承这个属性。另外要让此属性对connection和recordset对象的实例起作用的话,必须在打开它们之前先定义。
下面看一个例子
set conn=server.createobject("adodb.connection")
conn.cursorlocation=aduseclient
strconn ="driver={sql server};server=srv;"& _
"uid=sa;pwd=;database=pubs"
conn.open strconn
set rs= server.createobject("adodb.recordset")
rs.open "emloyee",conn,,,adcmdtable
采用次种方式则conn与rs的游标均为aduseclient了。
2、 connectionstring,在打开一个connection实例之前设定数据库的连接信息。在上面的例子中我们使用了一条语句conn.open strconn,其中的strconn就是connectionstring,因此我们可以重写上面的语句如下:
conn.connectionstring=strconn
conn.open
3、 connectiontimeout,设置连接超时。
4、 commandtimeout,设置命令执行超时。
connection的主要方法
1、 open,打开一个connection的对象实例,常用的写法为conn.open connectionstring,如果在打开之前已经定义了connectionstring属性的话,就可以直接的打开。
2、 execute,产生一个recordset实例,常用的写法为
rs=conn.excute commandtext,recordsaffected,option
其中的commandtext可以为以下的几种形式,主要由option的值来决定1) sql语句,此时option的值为adcmdtext,表示将执行一段sql语句。2)数据库的一个表名,此时option的值为adcmdtable,表示将对该表进行操作。
3、一个storedprocedure名字,此时option取值为adcmdstoredproc,它表示将要执行一个sql上定义的存储过程。这是一个非常灵活而强大的方法,它可以对用户隐藏数据库的具体信息,而只需用户提供适当的参数就可以了,还能返还需要的参数值。在后面介绍command对象时再做详细的介绍。值得注意的是,有时excute后并不需要返回一个recordset对象,例如在表中删除记录。看下面的例子:
set conn= server.createobject("adodb.connection")
conn.connectionstring="driver={sql server};server=srv;"& _
"uid=sa;pwd=;database=pubs"
conn.open
conn.execute "delect from employee where job_id=1;",,adcmdtext
该例子删除employee表中job_id为1的记录,并不需要再返回一个recordset的实例,但如果将最后一句变为rs= conn.execute "delect from employee where job_id=1;",,adcmdtext那么我们就可以用此rs来指向表中的记录条了。返回的rs和后面recordset对象中用source属性产生的rs是相同的。
connection对象的集合
1、 errors集合,对应产生error对象。我们将在后面做单独的讨论。
2、 properties集合,对应产生property对象,下面给出一段代码,它包含了property对象的主要方法和属性。
$#@60;%
"this program is testing the ado"s property object
const adcmdtable = &h0002
set conn=server.createobject("adodb.connection")
set rs=server.createobject("adodb.recordset")
conn.connectionstring="driver={microsoft access driver (*.mdb)};dbq="& _
server.mappath("/source_asp")&"/property/employee.mdb;"
conn.open
rs.activeconnection=conn
rs.open "employee",,,adcmdtable
dim i,j
for i=0 to rs.fields.count-1
response.write rs.fields(i).name&"$#@60;br$#@62;"
next
for j=0 to rs.properties.count-1
response.write rs.properties(j).name&"$#@60;br$#@62;"
next
rs.close
conn.close
%$#@62;
三、error对象:
前面讲到了connection对象是用于与各类的数据库进行挂接的,但在此过程中将会出现一些不可预测的错误,因而有了error这个对象。首先要清楚一个概念,error对象是在连接数据库时产生的,而并非那些运行时的实时错误。也就是我们常用 on error resume next来忽略到的错误。这些错误将在err对象中,我们可以用一个统一的模板来集中处理,我会在后面给出一个实例。下面还是先来看error对象的属性和方法:
1、 count属性:用来统计errors集合的数目,它的特点与前面讲到的property对象的count对象相同。
2、 clear方法:写法为error.clear,是用来清除errors集合中的原有对象的,在统计新的error对象时应该先使用此语句。
3、 item方法:用来指定特定的一个错误,语法为error.item(number),其中number为一数字。由于item为默认的方法,所以error(number)的写法与前面的写法是等价的。下面是一段程序。用来列举error的所有对象。
$#@60;%
"this program is testing the ado"s error object
dim i
set conn=server.createobject("adodb.connection")
conn.connectionstring="driver={microsoft access driver (*.mdb)};dbq=" _
&server.mappath("/source_asp")&"/property/employee.mdb;"
conn.open
if conn.errors.count$#@62;0 then
response.write "connection to database cause problem!"&"$#@60;br$#@62;"
for i =0 to conn.errors.count-1
response.write conn.errors.item(i)&"$#@60;br$#@62;"
next
else
response.write "connection to database successfully!"
end if
conn.close
%$#@62;
对于err对象的通用模板处理程序我将在讨论recordset对象时给出。
下面我们将讨论ado的第二个大的对象━command对象,我的一个做网站的朋友告诉我他在平时使用asp挂接数据库时很少使用command对象,原因是command对象不好使用,而喜欢用recordset对象。是的,可以这么说command对象是整个ado模型中最难掌握的一个,但也是功能和性能最好的一个。特别是它的storedprocedue,它将处理的过程大部分都使用了在sql server上已经编译和优化了的存储过程,用过sql server的朋友都会明白的。下面就让我们来看command对象。
四、command对象:
从英语字面的意思就可以看出,command是用来做命令执行和参数传递的。而command对象的批量参数传递,storedprocude执行等等灵活而强大的功能也是它受到青睐的原因。command对象主要是向sql语句、storedprocude传递参数,依靠sql server的强大功能来完成数据库的操作;而recordset对象可以说是微软从新封装了数据对象,并提供了一系列的方法和属性来简化数据库的编程。我们看下面的一个例子,它用了两种不同的方法实现了向数据库中增加一新的记录条。从中可以清楚的看到command对象与recordset对象的不同点。
方法1(command)
const adcmdtext=&h0001
const adinteger=3
const advarchar=200
const adparaminput = &h0001
set conn=server.createobject("adodb.connection")
set comm=server.createobject("adodb.command")
conn.open "driver={ microsoft access driver};dbq="& _
server.mappath("/source_asp")&"/property/employee.mdb;"
comm.activeconnection=conn
comm.commandtype=adcmdtext
comm.commandtext="insert into employee (job_id,fri_name,last_name)"& _
&"values(?,?,?)"
set param=comm.createparameter("id",adinteger,adparaminput,3,4)
comm.parameters.append param
set param=comm.createparameter("fn",advarchar,adparaminput,255,"bill")
comm.parameters.append param
set param=comm.createparameter("ln",advarchar,adparaminput,255,"gates")
comm.parameters.append param
comm.execute
conn.close
方法2(recordset)
const adcmdtable=&h0002
set conn=server.createobject("adodb.connection")
set rs=server.createobject("adodb.recordset")
conn.open "driver={microsoft access driver (*.mdb)};dbq="& _
server.mappath("/source_asp")&"/property/employee.mdb;"
rs.activeconnection=conn
rs.open "employee",,,adcmdtable
rs.addnew
rs("job_id")=4
rs("fri_name")="bill"
rs("last_name")="gates"
rs.update
rs.close
conn.close
从上面的例子就可以看出来了,这两个对象在处理一些问题上所用的不同的方法.recordset对象似乎更加好理解一些,因为它加入了一些在ansi sql中没有的元素,它其实是用 sql在数据库上产生一个记录集,然后用一个游标来指向这个记录集,超作该游标来遍历这个记录集。但在性能上来讲的话command的性能也相对要优越些.其可重应用性也非常的好。而且如果你是批量的加入记录的话,你也能体会到第一种方案的好处了,因为command对象就是将sql产生的记录集作为整体来处理。下面详细介绍command对象的属性、方法和集合。
1、 createparameter方法:用来产生一个parameter对象,常用的写法为set param=comm.createparameter(name,type,direction,size,value),其中name为参数的引用名,在后面引用参数的值时会有用;type为指定参数的类型,例如整数为adinteger;direction指定参数是输入还是输出,相应的值为adparaminput和adparamoutput;size指定参数的最大长度或最大的值;value指定参数的值。可以将各个选项分开来写,下面的两种写法是等价的:
set param= comm.createparameter(name,type,direction,size,value)
和
set param= comm.createparameter(name,type,direction,size)
param.value=value
下面的方法其灵活性更大。大家请注意,在使用了createparameter方法后只是建立了新的 parameter对象,还需使用parameter对象的append方法将该参数传递给command对象。
2、 execute方法:在指定了commandtext后,并将参数传递出去后,用execute方法来完成执行。
3、 activeconnection属性:用来指定与connection对象的连接,这里的一个技巧就是不同的command对象指向同一个connection连接。
4、 commandtext属性:其值可以是一条sql命令句,可以是一个表名,也可以是一个storedprocedure名。
5、 commandtype属性:它的值由commandtext相应值的给出,分别为adcmdtext,adcmdtable,adcmdstoredproc。与前面在讲connection对象的execute方法中的相应的选项的含义相同。
6、 commandtimeout属性:设定命令执行的超时的值。
7、 properties集合:我们不多讲了,与connection对象的property集合相差不多。
8、 parameters集合:也就是参数对象的集合了,他有主要item方法、append方法,和count属性,用法与property对象及error对象的相应属性和方法类似,下面给出一个示例:
const adcmdtext=&h0001
const adinteger=3
const advarchar=200
const adparaminput = &h0001
set conn=server.createobject("adodb.connection")
set comm=server.createobject("adodb.command")
conn.open "driver={microsoft access driver (*.mdb)};dbq="& _
server.mappath("/source_asp")&"/property/employee.mdb;"
comm.activeconnection=conn
comm.commandtype=adcmdtext
comm.commandtext="insert into employee (job_id,fri_name,last_name)"& _
"values(?,?,?);"
set param=comm.createparameter("id",adinteger,adparaminput,3)
param.value=14
comm.parameters.append param
set param=comm.createparameter("fn",advarchar,adparaminput,255,"bill")
comm.parameters.append param
set param=comm.createparameter("ln",advarchar,adparaminput,255,"gates")
comm.parameters.append param
comm.execute
conn.close
"the folowing statments show the value of parametrs
dim i
for i=0 to comm.parameters.count-1
response.write comm.parameters.item(i)&"$#@60;br$#@62;"
next
当然,我们在引用参数时也可以不用数字,而用前面在createparameter时定义的名字,例如:fn、id等等。另外我们可以将上面的程序的显示部分改为
dim key
for each key in comm.parameters
response.write key&"$#@60;br$#@62;"
next
下面我想重点讲一讲storedprocedure,它的强大足以让我们对它关注,当然这其中会涉及到一些sqlserver的知识
五、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.commandtype=adcmdtext
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的讨论上来。storedprocedure是什么呢?它是一个预先存储的数据库执行动作集,在sql的管理结构中,对于一个数据库下有几个部分,一个是数据表的集合、一个就是storedprocedure的集合。将两者结合可以完成很多强大的功能。storedprocedure其实是对传统的sql语句的一种扩展,主要是在参数的输入与输出上。下面我大致的介绍一下storedprocedure的语法结构和与command对象的参数的传递问题。
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 conn=server.createobject("adodb.connection")
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"
"这里的名字就是前面在sql server中定义过的storedprocedure的名字。
"下面就是参数的输入
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 conn=server.createobject("adodb.connection")
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"
"这里的名字就是前面定义过的storedprocedure的名字。
"下面就是参数的输入
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来完成的,例子如下:
create storedprocedure insert_loan
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
好了,这看上去好象没有什么不同吧,但需要注意的是我们现在将两个insert into语句作为了一个的事务来处理,只有两个insert into语句都完成的话才是一个整体的事务结束,那么它才会去作用这个数据库中的两个表,若在事务中发生了故障的话,则所有的影响将取消(roll back)。好了,这样的处理是只有在sql server中用storedprocedure才能完成的。ansi的sql当然就不行了。这里讲的大家可能不太明白,你可以参看sql server的手册来作更多的了解。
下面我们来看最后的一个对象─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对象之前来设定它。下面是源代码,为了方便,我将导航栏独立成了一个子程序方便大家使用。
$#@60;%
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;
$#@60;%
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;
$#@60;%
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;
