下载第 9章 连接、命令和过程上一章讨论了 A D O的基础知识,内容主要涉及 R e c o r d s e t对象以及对数据的处理。在大多数例子中,只是通过指定数据库的表名来获取数据,但正如从对象模型中看到的,A D O还有其他允许访问数据的对象。
本章将要更详细地介绍这些对象,特别将研究以下内容:
Connection对象,以及如何用它来获取数据和运行命令。
Command对象,为什么需要该对象及其所具有的功能。
如何运行存储过程,特别是那些需要参数的存储过程。
一些简单的改善 A D O性能的优化技巧。
数据整形的概念及如何使用。
如同介绍 R e c o r d s e t对象那样,我们不打算覆盖所涉及对象的全部方法和属性。在这里只探讨其中最重要的主题,以及那些适用于 A S P开发人员的方法与属性。
9.1 Connection对象前一章中已经提及,C o n n e c t i o n对象是为我们与数据存储提供连接的对象,但这并非
C o n n e c t i o n对象的全部功能。除了存储连接的细节外 (比如数据存储的类型,以及其支持的特性 ),也可以利用 C o n n e c t i o n对象运行命令。
这些命令可能是查询动作,比如更新、插入或删除操作,也可以是返回一个记录集的命令。读者可能会觉得奇怪:既然有了 R e c o r d s e t对象,这又有什么用?这正是 A D O的灵活性所在,可以根据当前的情况,以及对当前任务的适用性选择使用任一种对象。
从 C o n n e c t i o n对象运行的命令一般是查询动作,但了解能够得到返回的记录集也是非常有用的。
9.1.1 返回一个记录集为了从 C o n n e c t i o n对象返回一个记录集,要使用 E x e c u t e方法。语法是:
Connection.Execute CommandText,[RecordsAffected],[Options]
参数及说明如表 9 - 1所示。
表 9-1 Connection对象的 E x e c u t e方法的参数及说明参 数 说 明
C o m m a n d s Te x t 执行的命令文本。与 R e c o r d s e t的 O p e n方法中的 S o u r c e参数相同,也能代表一个现有的 C o m m a n d对象
R e c o r d s A ff e c t e d 受命令执行影响的记录数
O p t i o n s 命令选项,可以是一个或多个 C o m m a n d Ty p e E n u m或 E x e c u t e O p t i o n E n u m常数,详细的值请见上一章
Execute方法可选择地返回一个记录集,在这种情况下只要将返回值赋给记录集变量。例如:
读者可能会奇怪使用 C o n n e c t i o n对象的 E x e c u t e方法与使用 R e c o r d s e t对象的 O p e n方法之间到底有什么区别?看上去区别不是很大,使用 R e c o r d s e t对象的 O p e n方法可以改变光标类型和锁定类型。这些选项对于 C o n n e c t i o n对象的 E x e c u t e方法是不可用的,因此永远只能得到一个只能前移的、只读的记录集。
9.1.2 操作命令如果正在运行操作命令,比如一个 SQL UPDAT E语句,那么可以使用 R e c o r d s A ff e c t e d参数找出有多少条记录受到该命令的影响。例如:
上述代码将所有类型为 B u s i n e s s的书的单价增加了 1 0 %。一旦 E x e c u t e命令执行完毕,受
U p d a t e命令影响的记录数就返回到变量 l n g R e c s中,这就是 R e c o r d s A ff e c t e d参数。
注意,已经为命令指定了 a d C m d Te x t选项,告诉 A D O C o m m a n d Te x t参数是一个文本命令。
一般这不是严格必须的,其目的只是让 A D O预先知道执行的命令属于何种类型,这样能够提高性能。记住,这个值可以是一个或多个 C o m m a n d Ty p e E n u m值。
无记录集返回如果上面的例子不返回记录集,那么在 E x e c u t e语句中加入另一个选项也是较好的方法:
使用 a d E x e c u t e N o R e c o r d s告诉 A D O执行的命令不返回任何记录。所以,A D O不必费心去创建一个记录集。如果省略了该选项,那么 A D O将会创建一个空的记录集,很明显这浪费了时间,因此加上这个选项将会加快语句的执行。
9.2 Command对象
C o m m a n d对象特定地为处理各种类型的命令而设计,特别是那些需要参数的命令。与
C o n n e c t i o n对象相似,C o m m a n d对象可以运行返回记录集和不返回记录集两种类型的命令。
实际上,如果命令不含有参数,那么它并不关心是使用 C o n n e c t i o n对象,还是 C o m m a n d对象,
还是 R e c o r d s e t对象。
9.2.1 返回记录集对于一个返回记录集的命令,可使用 E x e c u t e方法。然而,与 C o n n e c t i o n对象不同,必须
274计计 ASP 3 高级编程 下载使用 C o m m a n d Te x t属性,而不能在 E x e c u t e方法中使用命令文本。
这是告诉 C o m m a n d对象去执行一个简单的、返回一个只读记录集的命令的最简便方法。
E x e c u t e方法也有一些可选参数,如表 9 - 2所示。
表 9-2 Command对象的 E x e c u t e方法的参数及说明参 数 说 明
R e c o r d s A ff e c t e d 受命令影响的记录数
P a r a m e t e r s 参数值数组
O p t i o n s 命令选项,与 R e c o r d s e t的 O p e n方法中的 O p t i o n s选项相似参数 R e c o r d s A ffected 与 O p t i o n s同前面解释的一样,另外也可以使用 C o m m a n d Ty p e属性设置命令类型:
如果不设置其他参数,也可以在 E x e c u t e行上设置,必须为它们使用逗号:
在本章后面处理存储过程和参数时,将会看到参数 P a r a m e t e r s的用途。
改变光标类型值得注意是,使用 E x e c u t e方法返回的记录集具有缺省的光标和锁定类型。这意味着这是只能前移的、只读的记录集。虽然使用 E x e c u t e方法不能改变这种情况,但对这个问题有一个解决的方法。
如果需要使用一个命令,并且要求不同的光标和锁定类型,那么应该使用 R e c o r d s e t的
O p e n方法,此时 C o m m a n d对象作为 R e c o r d s e t的数据源。例如:
注意,在 O p e n命令行中忽略了连接细节,因为连接设置在 C o m m a n d对象中。连接细节在命令打开前已经设置在 C o m m a n d对象的 A c t i v e C o n n e t i o n属性中。
9.2.2 操作命令对于操作命令,比如那些无记录返回的更新命令,整个过程相似,只需移去设置记录集的代码:
第 9章 连接、命令和过程 计计 275下载注意,我们在此设置了命令类型,然后在 E x e c u t e行中增加了额外的设置选项。这段代码运行 U P D AT E命令,并且保证不会创建新的记录集。
9.2.3 存储过程存储过程的使用是 C o m m a n d对象得到应用的一个领域。存储过程 (有时也称存储查询 )是存储在数据库中预先定义的 S Q L查询语句。
为什么应该创建和使用存储过程而不是在代码中直接使用 S Q L字符串呢?主要有以下几个理由:
存储过程被数据库编译过。这样可以产生一个“执行计划”,因此数据库确切地知道它将做什么,从而加快了过程的执行速度。
存储过程通常被数据库高速缓存,这样使它们运行得更快,因为此时不需从磁盘中读取它们。并非所有的数据库都支持这种缓存机制,比如微软的 Access 就不支持,而 S Q L
S e r v e r却支持。
通过指定数据库中的表只能被存储过程修改,可以确保数据更安全。这意味着具有潜在危险的 S Q L操作不会执行。
可以避免将 A S P代码和冗长的 S Q L语句混在一起,从而使 A S P代码更易于维护。
可以将所有 S Q L代码集中存放于服务器。
可以在存储过程中使用输出参数,允许返回记录集或其他的值。
一般说来,存储过程几乎总是比相当的 S Q L语句执行速度快。
为了使用存储过程,只要将存储过程的名字作为命令文本,并设置相应的类型。例如,
考虑前面更新书价的例子。如果在 SQL Server上创建一个存储过程,可以编写代码:
对于微软的 A c c e s s数据库,可以使用一个简单的更新查询语句完成相同的任务,如图 9 - 1所示。
要在 A S P网页中运行该存储过程,只需使用以下代码:
这只是运行存储过程。没有记录集返回,因为只是在更新数据。需要记住的是,除非确
276计计 ASP 3 高级编程 下载图 9-1 使用微软的 A c c e s s
数据库完成更新查询实需要,不要创建记录集。
虽然这样做也可以,但并不是很灵活,因为仅仅处理一种类型的书。更好的做法是创建一个允许我们选择书类型的过程,这样就不必为每类书创建一个过程。同样也可去掉固定的
1 0 %的更新,这样使得灵活性更好。那么,如何才能做到这一点呢,很简单,使用参数。
1,参数存储过程的参数 (或变量 )与一般的过程和函数的参数一样,可以传到函数内部,然后函数可以使用它的值。 SQL Server(其他数据库也一样,包括 A c c e s s )中的存储过程都具有这样的功能。
为了使存储过程能处理多种类型的书,甚至允许用户指定价格的增加 (或减少 ),需要增加一些参数:
现在,存储过程 u s p _ U p d a t e P r i c e s带有两个参数:
一个是书的类型 ( @ Ty p e )。
一个是书价变化的百分比 ( @ P e r c e n t )。
与 V B S c r i p t的函数一样,这些参数都是变量。然而,与 V B S c r i p t和其他脚本语言不同是:
在这些脚本语言中的变量都是 v a r i a n t类型,而 S Q L变量具有确定的类型 ( c h a r,M o n e y等等 )。
必须遵守 S Q L变量的命名规范,即变量必须以符号 @开始。
注意,我们让百分数作为一个整数 (如 1 0代表 1 0 % ),而不是作为一个分数值传入此过程。
这只是让存储过程变得更直观一些。
2,Pa r a m e t e r s集合那么,现在有了带参数的存储过程,但如何通过 A D O来调用它呢?我们已经见到了如何用 C o m m a n d对象调用不带参数的存储过程,实际上,它们之间并没有什么不同。不同之处在于 Pa r a m e t e r s集合的使用。
Pa r a m e t e r s集合包含存储过程中每个参数的 Pa r a m e t e r对象。然而,A D O并不会自动地知道这些参数是什么,因此,必须用 C r e a t e P a r a m e t e r方法创建它们,采用下面的形式:
参数及说明如表 9 - 3所示。
表 9-3 CreateParameter方法的参数及说明参 数 说 明
N a m e 参数名。这是 P a r a m e t e r s集合中的参数名,不是存储过程中的参数名。然而,使用相同的名字是一个好的做法
Ty p e 参数的数据类型。可以是一个 a d D a t a Ty p e常数,详见附录
D i r e c t i o n 参数的方向,指明是参数向存储过程提供信息,还是存储过程向 A D O返回信息。可以是下面的值之一:
第 9章 连接、命令和过程 计计 277下载
(续 )
参 数 说 明
a d P a r a m I n p u t,参数是传给存储过程的输入参数
a d P a r a m O u t p u t,参数是从存储过程检索出的输出参数
a d P a r a m I n p u t O u t p u t,参数可同时作为输入和输出参数
a d P a r a m R e t u r n Va l u e,该参数包含存储过程返回的状态
S i z e 参数长度。对于固定长度的类型,比如整型,该值可以忽略
Va l u e 参数的值一旦创建了参数就可以将其追加到 P a r a m e t e r s集合中,例如:
没有必要显式地创建一个对象去保存参数,缺省的 Va r i a n t类型已经可以工作得相当好。
如果不想创建一个变量,也可以走捷径,例如下面的代码:
这使用 C r e a t e P a r a m e t e r方法返回一个 P a r a m e t e r对象,并用 A p p e n d方法接收它。这种方法比使用变量运行得快,却加长了代码行,可读性比较差。可以根据自己的爱好选择其中一种方法。
参数加到 P a r a m e t e r s集合后,就保留在其中,因此,不一定在创建参数时就为每个参数赋值。可以在命令运行前的任何时候设置参数的值。例如:
前一章提到了访问集合中的值有好几种方法,P a r a m e t e r s集合并没有什么不同。上面的例子使用参数的名字在集合中检索参数,也可以使用索引号进行检索:
以上代码对参数集合中第一个 ( P a r a m e t e r s集合从 0开始编号 )参数进行了赋值。使用索引号比使用名字索引速度快,但很显然使用名字使代码更易读。
重点注意 P a r a m e t e r s集合中参数的顺序必须与存储过程中参数的顺序相一致。
运行带参数的命令一旦加入参数,就可立即运行命令,同时这些参数的值传入存储过程。现在可用一个友好的网页去更新用户选择的类型的书价。例如,假设有一个名为 U p d a t e P r i c e s,a s p的网页,其运行时的界面如图 9 - 2所示通过从数据库中获取书类型的列表,可以很轻松地动态创建该页面。首先要做的是包含文件 C o n n e c t i o n,a s p,该文件包含了连接字符串 (保存在变量 s t r C o n n中 )以及对 A D O常数的引用,
278计计 ASP 3 高级编程 下载这在前面的章节已经讨论过。
图 9-2 UpdatePrices.asp网页运行时的界面接下来,可以创建一个窗体 (在这儿不显示大量文本,仅仅用一个样本文件 )。该窗体调用一个名为 S t o r e d P r o c e d u r e,a s p的文件。
现在开始编写 A S P脚本从 t i t l e s表中读取书的类型。使用一个 S Q L字符串只返回唯一的书类型,然后将返回值放到 H T M L的 O P T I O N标记中:
第 9章 连接、命令和过程 计计 279下载显示书的类型后,接着可以构建窗体的其他部分,包括一个允许用户输入书价变化百分数的文本框。
现在看一下 Run Query按钮调用的 A S P文件 S t o r e d P r o c e d u r e,a s p。首先,声明变量并从调用窗体取出书的类型和百分数。
现在可以向用户显示一些确认信息,告诉他们将发生什么。
现在重新回到代码内部,在此创建 C o m m a n d对象和参数。
利用从前面网页的窗体中提取的数据值,使用快捷方法创建和增加参数。
现在,运行存储过程。
280计计 ASP 3 高级编程 下载为了确认,可以告诉用户已经更新多少条记录。
这样就有了两个简单页面。前者创建了一个供选择的项目列表,后者使用其中某个项目值更新数据。这是许多需要显示和更新数据的 A S P页面的基础。
3,传递数组参数
P a r a m e t e r s参数集合一般来说比较好用,但有时稍有麻烦 (尤其对于新手 )。好在有一种快捷方法,使用 E x e c u t e方法的 P a r a m e t e r s参数。例如,调用存储过程 u s p _ U p d a t e P r i c e s,但不使用 P a r a m e t e r s集合。
创建一个 C o m m a n d对象,并同前面一样设置其属性。
但这里正是差异所在。我们仅是通过 E x e c u t e方法传递参数给存储过程,而不是创建参数并添加到集合中。
这里使用了 A r r a y函数,将单个变量转换为数组,以适于方法调用。这种方法当然也有缺点:
只能使用输入参数。因为不能指定参数的类型和传递方向,而缺省为输入参数。
如果要多次调用存储过程,这种方法速度就比较慢,因为 A D O将向数据存储询问参数的内容及数据类型。
集合方法和数组方法之间在速度上的差异非常之小,几乎可以忽略。所以,如果只有输入参数,可随便使用哪一种。实际上,人们更喜欢使用 P a r a m e t e r s集合的方法,尽管它稍为繁琐,但是使参数的属性更加明确。
4,输出参数我们已经知道如何获得受命令影响的记录数,如果需要更多信息,却又不想返回一个记录集,怎么办?也许想从存储过程中返回两个或三个值,但又不想费心创建一个记录集。在这时,可以定义一个输出参数,其值由存储过程提供。
例如,对于更新书价的程序,如果想在更新之后找出最高价格,可将存储过程改成:
第 9章 连接、命令和过程 计计 281下载这只是在执行更新后运行了一个简单的 S E L E C T语句,并将值赋给输出参数。
现在可以改写 S t o r e d P r o c e d u r e,a s p的代码从而获取变量 @ M A X的值。
我们只是在集合中加入了另一个参数,但这次指定为输出参数。注意它并没有赋值,因为其值将由存储过程提供,记住这是一个输出参数。
一旦执行了这个过程,就可从集合中取得该值。
282计计 ASP 3 高级编程 下载如果有不止一个输出参数,可用相同的方法访问。可以使用参数名或索引号取出集合中的值。
5,返回值对函数返回值的处理不同于存储过程返回值的处理,这常常导致混淆。在函数中,经常是返回一个布尔值来表明函数运行的成功与否。
但在调用一个存储过程时,却不能使用同样的方法,因为存储过程是用 E x e c u t e方法运行的,同时返回一个记录集。
如果得不到一个返回值,如何确定是否已正确执行存储过程?当发生错误时,会报告错误,这样就可使用前一章提供的错误处理代码来处理错误。但对于一些非致命的逻辑错误怎么办?
例如,考虑向 e m p l o y e e表添加一个新职员的情形。你可能不想防止两个职员同名的情况,
但想注明这个情况。那么,可以使用一个返回值以表明是否已有同名的职员存在。存储过程如下:
该过程首先检查是否有同名的职员存在,并据此设定相应的变量 E x i s t s,若存在同名,就设为 1,否则为 0。然后将该职员加到表中,同时把 E x i s t s的值作为返回值返回。
第 9章 连接、命令和过程 计计 283下载注意尽管返回了一个值,但并未将其声明为存储过程的参数。
调用该过程的 A S P代码如下:
需要重点注意,返回值应当作为集合中第一个参数被创建。即使返回值并不作为一个参数出现在存储过程中,总是 P a r a m e t e r s集合中的第一个 P a r a m e t e r s。
284计计 ASP 3 高级编程 下载因此,特别强调一点:
存储过程的返回值必须声明为 P a r a m e t e r s集合中第一个参数,同时参数的
D i r e c t i o n值必须为 a d P a r a m R e t u r n Va l u e。
使用返回值现在定义一个初始窗体,如图 9 - 3所示。
图 9-3 初始窗体界面按下 Add Employee按钮会产生如图 9 - 4所示的显示。
图 9-4 按下 Add Employee按钮后显示的界面再添加同样的细节 ( I D号不同 )会得到如图 9 - 5所示的显示。
6,更新参数无需输入所有的参数细节,只需调用 R e f r e s h方法,就能让 A D O完成更新。例如,假设已经创建了一个带有与前面例子相同的参数的过程 u s p _ A d d E m p l o y e e,并且没有改变运行的页面。
第 9章 连接、命令和过程 计计 285下载图 9-5 添加细节后显示的界面然后调用 R e f r e s h方法。
这告诉 A D O向数据存储请求每个参数的细节,并创建 P a r a m e t e r s集合。然后可以为其赋值。
注意并不需要创建任何参数,包括返回值。
这似乎真是一条捷径,但应意识到这种方法也造成了性能上的损失,因为 A D O必须向提供者查询以获得存储过程的参数细节。尽管如此,这种方法还是很有用的,尤其是在从参数中取出正确的值有困难的时候。
实际上,可以编写一个小实用程序作为开发工具使用,用来完成更新并建立 A p p e n d语句,可以将其粘贴到自己的代码中。它看上去应该与图 9 - 6所示的 GenerateParameters.asp ASP页面类似。
图 9-6 GenerateParameters.asp ASP页面
286计计 ASP 3 高级编程 下载其代码相当简单。首先是包含连接字符串和另一个 A D O X常数文件。
接下来创建一个窗体,指定目标为 P r i n t P a r a m e t e r s,a s p A S P页面。
然后,使用 A D O X从 SQL Server中得到存储过程的列表,同时创建一个含有这些存储过程名字的列表框。
这是一个简单的窗体,包括一个用于显示连接字符串的 T E X TA R E A控件和用于显示存储过程名称的 S E L E C T控件。以前没有见过的是 A D O X,A D O X是数据定义与安全的 A D O扩展,
可以用来访问数据存储的目录 (或是元数据 )。
本书不打算 A D O X 的内容,但其十分简单。进一步的细节可参见,A D O
Programmer's Reference》,Wr o x出版社出版,2,1版或 2,5版都行。
上面的例子使用了 P r o c e d u r e s集合,这个集合包含数据存储中的所有存储过程的列表。按下 Print Parameters按钮时,将得到图 9 - 7所示的显示。
可以简单地从这里拷贝参数行到代码中。在前面使用了一个以前从未见过的包含文件。
该文件包含了几个将 A D O常数 (例如数据类型、参数方向等 )转换为字符串值的函数:
<!-- #INCLUDE FILE= "../Include/Descriptions.asp" -->
第 9章 连接、命令和过程 计计 287下载图 9-7 按下 Print Parameters按钮时显示的界面接下来,定义一些变量,提取用户请求并创建 C o m m a n d对象。
然后使用 R e f r e s h方法自动填写 P a r a m e t e r s集合。
现在可以遍历整个集合,写出包含创建参数所需的细节内容的字符串。
288计计 ASP 3 高级编程 下载在 D e s c r i p t i o n s,a s p包含文件中可以找到函数 D a t a Ty p e D e s c和 P a r a m D i r e c t i o n D e s c。
D e s c r i p t i o n s,a s p包含文件以及其他的例子文件可以在 We b站点 h t t p,/ / w w w.
w r o x,c o m中找到。
这是一个非常简单的技术,它较好地使用了 R e f r e s h方法。
9.3 优化优化是每个开发人员应该关心的问题。对于数据库访问,优化是一个关键问题。和其他任务相比,数据的访问显得相对慢些。
因为数据访问的变化是如此之多,以致于几乎不可能提出一套固定的数据库操作的优化规则。通常碰到这类问题,经常得到这样的回答:,这取决于?”,因为这类优化问题取决于准备做什么。
9.3.1 常用的 A D O技巧尽管优化取决于所执行的任务,但是仍然有一些常用的技巧:
仅选择所需的列。当打开记录集时,不要自动地使用表名 (即 SELECT *),除非需要获得所有的列。使用单独的列意味着将减少发送到服务器或从服务器取出的数据的数量。即使需要使用全部列,单独地命名每个列也会获得最佳的性能,因为服务器不必再解释这些列是什么名字。
尽可能使用存储过程。存储过程是预先编译的程序,含有一个已经准备好的执行计划,
所以比 S Q L语句执行得更快。
使用存储过程更改数据。这总是比在记录集上使用 A D O方法执行速度快。
除非必需否则不要创建记录集。运行操作查询时,要确定加入了 a d E e x e c u t e N o R e c o r d s
选项,这样记录集就不会创建。当仅仅返回一个或两个字段的单行记录时 (比如 I D值 ),
也可以在查询状态下使用这种方法。在这种情况下,存储过程和输出参数将会更快。
使用适当的光标和锁定模式。如果所做的全部工作是从记录集中读取数据,并将其显示在屏幕上 (比如,创建一个表 ),那么使用缺省的只能前移的、只读的记录集。 A D O用来维护记录和锁定细节的工作越少,执行的性能就越高。
9.3.2 对象变量当遍历记录集时一个保证能提高性能的方法是使用对象变量指向集合中的成员。例如,
考虑下面的遍历含有 A u t h o r s表的记录集的例子。
可以用下面的方法加速代码执行,同时使其更易于理解。
第 9章 连接、命令和过程 计计 289下载这里使用了两个变量,并指向记录集的 F i d d s集合中的特定字段 (记住,F i d d s集合是缺省的集合 )。因为这里建立了一个对象的引用,所以可以使用对象变量而不是实际的变量,这意味着脚本引擎的工作减少了,因为在集合中进行索引的次数变少了。
9.3.3 高速缓存大小高速缓存的大小是指 A D O每次从数据存储中读取的记录的数量,缺省为 1。这意味着当使用基于服务器的光标时,每当移动到另一条记录时,必须从数据存储中提取记录。举一个例子,如果增大高速缓存的大小为 1 0,那么每次读入 A D O缓冲区的记录数将变为 1 0。如果访问位于高速缓存内的记录,那么 A D O不需要从数据存储中取记录。当访问位于高速缓存外的记录时则下一批记录将读入到高速缓存中。
通过使用记录集的 C a c h e S i z e属性,可以设置高速缓存的大小。
可以在记录集生命期的任何时候改变高速缓存的大小,但新的数量只在提取下一批记录后才有效。
与许多改进性能的技巧类似,高速缓存没有通用的最佳大小,因为它随任务、数据和提供者的不同而改变。但是,从 1开始增加高速缓存的大小总是能提高性能。
如果你想看到这一点,可以使用 SQL Server Profiler并查看使用缺省的高速缓存打开一个记录集发生的情况,并比较增大高速缓存后发生的情况。增大高速缓存的大小不仅减低了 A D O的工作量,同时也降低了 SQL Server的工作量。
9.3.4 数据库设计不要希望只通过编程来提高对数据的访问效率,应该同时考虑一下数据库的设计。这里并不打算对数据库设计进行更多的讨论,但在使用 We b站点数据库时应考虑以下几点:
实时数据:向用户显示数据时,确保数据内容总是最新是十分重要的。以一份产品目录为例,目录内容改变的频率有多快?如果该目录并非经常改变,那么不必每次都从数据库中提取数据。每周一次,或在数据改变时从数据库产生一个静态的 H T M L页面应是一个更好的办法。
索引:如果需要对表进行大量的查询,而不执行太多的添加数据操作,那么可以考虑为表建立索引。
不规范化:如果站点有两个不同的目的 (数据维护与数据分析 ),那么可以考虑采用一些不规范化的表以便有助于数据的分析。可以提供独立的、完全不规范化的但能正常更新的分析用表,为了改善性能甚至可以将这些分析表移到另一台机器上。
数据库统计:如果使用的是 SQL Server 6.x,如果数据被添加或删除,那么应定期更新统计结果。这些统计结果用于产生一个查询计划,会影响查询的运行。请阅读 S Q L
290计计 ASP 3 高级编程 下载
Books Online中的 U P D ATE STAT I S T I C以便了解更详细的内容。在 SQL Server 7.0中这一任务自动完成。
这些都是十分基本的数据库设计技巧,但若只埋头于 A S P代码可能不会考虑到这些。
9.3.5 数据高速缓存首先需要注意的是,数据高速缓存与记录集高速缓存虽然都用于改善性能,但两者是无关的。数据高速缓存是临时的数据存储区,允许使用高速缓存中的数据,而不是重新生成新的数据。这只适用于那些不经常改动但多次被访问的数据。
在 A S P中一个最简单的缓存数据的方法是使用 A p p l i c a t i o n和 S e s s i o n范围的变量。例如,
假设有一些需要选择书类型的网页。正常情况下,可能会创建一个含有以下函数的包含文件。
这仅仅是调用一个存储过程,从而得到书的类型,同时创建一个 S E L E C T列表。上述代码的缺点在于每次调用该函数都必须访问数据库。因此,重新修改这个函数。
第 9章 连接、命令和过程 计计 291下载这段代码不只是打开记录集,它检查 A p p l i c a t i o n变量 B o o k Ty p e的值是否为空。如果不为空,则使用该变量的内容。如果为空,则像以前一样打开记录集。显然,一旦第一个人运行了这一例程,便缓存了数据,因此这只对那些不常变化的数据是有用的。
如果想在用户基础上缓存数据,可以使用 S e s s i o n范围的变量,但这里必须注意 S e s s i o n存在有效期。过期后会话层变量将和会话一起取消,代码便有可能终止运行。
利用 Web Application Stress(WA S )工具,得到了表 9 - 4的分析结果。
很明显性能有所改善。但不要采用上述方法缓存一切内容。毕竟,这种方法只适用于那些已经格式化后用于显示的数据。除此之外,还要考虑到如果 We b服务器只为特定的一个人服务,那几乎不是一个典型的 We b服务器的用法。使用
WA S可以在一个服务器上模拟多个用户,这样可以更实际地测试应用程序。
通过模拟一定数量的用户,Web Application Stress工具可以对 We b页面进行承受力测试。该工具有一个简单的图形界面,使用起来非常容易。可以从 h t t p,
/ / h o m e r,r t e,m i c r o s o f t,c o m /获得更多的信息,也可以下载该工具。
高速缓存对象若要缓存未格式化的数据该怎么办?可以在不同地方以不同的方式使用吗?当然,也可以用 A p p l i c a t i o n或 S e s s i o n变量这样做。考虑一下书标题的情况。你或许希望在多个页面中使用这个标题,也许在一个表格中显示所有的标题,或在一个列表框中显示供用户选择等等。
你可能会想到可以缓存记录集本身而无需缓存含有标签的 H T M L文本。
可以在 A p p l i c a t i o n或 S e s s i o n变量中缓存对象,但有两个主要的问题需要注意:
存放在 A p p l i c a t i o n变量中的对象必须支持自由线程,因此必须是自由线程对象或双线程对象。这意味着无法在 A p p l i c a t i o n变量中缓存由 V B创建的组件。
在 S e s s i o n状态中存放单元线程对象意味着创建该对象的线程是唯一允许访问它的线程。
因此 I I S无法较好地完成线程管理,因为任何试图访问这个对象的页面都必须等待原有线程服务于该页面。这将扼杀扩展应用程序的任何机会。
对于线程问题的讨论参见第 1 5章。
默认情况下,A D O作为单元线程对象装载,这主要是因为部分 OLE DB提供者并非是线程安全的。在 A D O安装目录中有一个注册表文件,可将 A D O转换成双线程模型,由此使 A D O
292计计 ASP 3 高级编程 下载表 9-4 利用 WA S工具得到的分析结果方 法 页面点击次数没有高速缓存 1 9 0
有高速缓存 11 0 0 0
对象可以安全地存放在 A p p l i c a t i o n和 S e s s i o n对象中。
你也许会认为所有的问题都解决了,可以通过使用各种类型的对象获得显著的速度提升,
但这并不一定。许多人已经认识到既然连接到数据库是一个相对昂贵的操作,那么缓存
C o n n e c t i o n对象可在再次连接时节省大量的时间。的确如此,但缓存 C o n n e c t i o n对象意味着该连接永远不会关闭,因此连接缓存池的工作效率比较低。连接缓存池隐含的一个思想实际上是减少服务器上使用的资源,而缓存 A S P状态中的对象显然不能减少资源的使用。事实上还增加了对它们的占用,因为每缓存一个对象便要占用服务器的资源。对于一个繁忙的站点而言,这将极大地降低 We b服务器的效率。
所以不应存储 C o n n e c t i o n对象,但对于 R e c o r d s e t对象,特别是断开连接的记录集呢?假定 A D O已从单元线程变成了双线程,就没有什么理由不这么做了,只要确切知道自己在做什么。不要认为这会自动地改善 A S P页的性能。每一个缓存的记录集都在内存和 A S P管理方面占用服务器的资源,因此不要缓存大的记录集。
另一个技巧是使用记录集的 G e t R o w s方法,将记录集转换成一个数组。因为数组并不像
R e c o r d s e t对象那样受线程问题的影响,因此非常适合用于会话层的变量。然而它同样也占用服务器资源,还必须考虑处理数组的时间。
构建自己的应用程序,缓存技巧并非是必要的。
9.4 数据整形数据整形或分层的记录集能显示一个树状结构或相关记录集。这通过在记录集的字段中包含一个记录集来实现,可以展现数据库的关系,而且多个记录集能在一次调用中返回。有两个理由可以解释它为什么是有用的:
性能:当正确使用时,数据整形可以改善性能。
便利:在数据整形中非常容易映射父子关系。
要知道数据整形涉及到哪些内容,最简单的方法是看图 9 - 8所示的内容。
图 9-8 数据整形涉及的内容第 9章 连接、命令和过程 计计 293下载图 9 - 8显示了 p u b s数据库中表 P u b l i s h e r s,Ti t l e s及 S a l e s的层次关系。
值得注意的一点是每个子记录集都不是独立的记录集。因此,图 9 - 8中只有三个记录集,
而不是六个。这是怎么来的呢?在层次关系中每一层都有一个记录集,分别是 P u b l i s h e r s、
Ti t l e和 S a l e s。在 P u b l i s h e r s表中引用标题时,实际上是引用了 Ti t l e s记录集,但 A D O过滤了
Ti t l e s,所以只显示那些与被选择的出版社对应的记录。这就容易使人误以为每个子元素有一个独立的记录集。
9.4.1 使用数据整形应用数据整形必须:
使用 MSDataShape OLEDB提供者。
使用一种特殊的整形语言,它是 S Q L的一种扩充,允许构造层次。
尽管使用新的提供者,连接字符串的实际改变不会太大。这是因为仍然需要从某处获取数据。因此,可以这么做:
这里用 M S D a t a s h a p e作为提供者,而正常的 P r o v i d e r变为 Data Provider,而连接字符串的剩余部分保持不变。
为数据整形创建连接字符串的简便方法是从创建正规的连接字符串开始,然后附加到数据整形块的最后。例如,考虑以下正规的连接字符串。
可以像下面这样为数据整形提供者创建连接字符串。
这将提供者设置为 M S D a t a S h a p e,而 Data Provider成为实际的数据源。初始的连接字符串已经包含 "Provider = ",所以为了获得正确的连接细节,只须在前面加上 D a t a。
1,整型语言整型语言有其自己的语法,但这里我们不打算涉及其构造,它已包含在 A D O文献中。大多数情况下会采用以下命令。
要理解这一点,最简单的方法是看一个实例,以 P u b l i s h e r s和 Ti t l e s为例。
第一行是父记录集,第二行则是子记录集。
第三行指明了关联父、子记录集的两个字段。
这个例子中两个表都有一个名为 P u b _ I D的字段
(出版社 I D字段 )。这个命令返回一个包含出版社的记录集,在记录集的最后又附加了一个含
294计计 ASP 3 高级编程 下载图 9-9 执行 S H A P E命令后的结果有子记录集的新列 (类型为 a d C h a p t e r )。该列名由 A S子句给出,在本例中是 r s Ti t l e s。
A d C h a p t e r类型只是说明了该字段含有一个子记录集。我个人认为,a d C h i l d或
a d R e c o r d s e t更合适。
通过遍历 F i e l d s集合,可以很容易看到父记录集的字段的情况。对于上面的 S H A P E命令,
得到图 9 - 9所示的结果。
访问子记录集现在,我们有了一个子记录集,它是另一个记录集的一个字段,那么如何访问这个子记录集呢?很简单,可以使用字段的 Va l u e属性来建立另一个记录集。
可以遍历父记录集,对应于每个父记录可以得到一个子记录集。下面的代码能实现这一点。通常以包含文件、变量声明开始。
现在创建连接字符串。
接下来,输入实际的整形命令。这将创建一个包含出版社的父记录集和一个含有书名的子记录集。
然后正常打开记录集。
像正常的记录集一样,能够遍历记录。
为了访问子记录集,设置一个变量来指向那个包含子记录集的字段的 Va l u e值。本例中该变量为 r s Ti t l e s。
变量 r s Ti t l e s在这里是个记录集,其作用同普通的记录集相同。因此,可以遍历该记录集的值,该值只包含与父出版者匹配的书名。
第 9章 连接、命令和过程 计计 295下载于是得到一份令人满意的出版社与书名的列表,如图 9 - 1 0所示。
图 9-10 整形后的书名列表用一些 D H T M L代码和一些额外的标记,就能轻松地隐藏起书名,并且只有选择出版社时才显示相应的书名。
2,多个子记录集如果对于每个记录集仅能有一个子记录集,那么数据整形就不够完善。但数据整形是极其灵活的。例如可以使用下面的程序来为出版社引用标题和雇员。
296计计 ASP 3 高级编程 下载只需在 A P P E N D子句后加上其他子记录集,就能得到图 9 - 11的结果。
图 9 - 11 多个子记录集的结果访问子记录集的方法并没有改变,仍旧可以用列的 Va l u e属性访问子记录集。只是此时有两个子记录集,因此需要使用两个变量。
3,孙代记录集在子记录集自身还包含子记录集的情况下,可能会出现孙代记录集。例如:
在第一个 A P P E N D子句内是另一个 S H A P E命令,而不是一个 S E L E C T语句。与多个子记录集的例子相似,访问孙代记录集的方法是相同的。
对子和孙记录集的深度没有理论上的限制,但也不太可能建立多于三级或四级的子记录集。
9.4.2 性能数据整形不会自动改善性能,但正确使用时可以改善性能。重要的是记住其工作方式:
第 9章 连接、命令和过程 计计 297下载
对于 S H A P E命令中的 S E L E C T语句,将完全取出表中的数据。 S Q L语句并没有得到任何优化。这样,如果在父表中加入 W H E R E子句来限制父记录集的记录数,仍能得到所有的子记录集。例如:
A P P E N D语句返回所有的标题,并不仅限于加州 ( C A )的出版社。记住,这不是 SQL JOIN
语句。在加州的出版社以及所有的标题都被提取了,这样就完成了数据整型。
可以使用存储过程,这会提高一点性能。然而,如果使用一个参数化的存储过程产生子记录集,那么每次访问子记录集时,这个存储过程都会执行。这意味着,子记录集不在前端代码中编程产生,而是只包含存储过程产生的那些记录。不足之处是增加了服务器的工作,但这样却能保证数据是最新的,因为每次需要时就从数据库中提取数据。
在下一章当我们着眼于客户端数据时,会看到更多有关经过整形的记录集的介绍。
9.5 小结本章主要涉及以下三个内容:
C o m m a n d对象,以及使用存储过程和参数。对于 A D O程序员来说,这是一个极为重要的主题,因为可以轻松提高应用程序的性能。通过存储过程将 S Q L代码置于服务器上,
得到了最大程度的优化,同时可以控制存储过程的数据输入和输出。
性能。这个话题是必不可少的,设计 A S P应用程序时考虑其性能是很重要的。通过改写代码来改善性能通常是很困难的,最好一开始就考虑性能而进行相应的编码。关于性能最重要的是测试,测试,再测试。测试网站性能的一个好的简单方法就是使用 We b
Application Stress工具。
数据整形。它虽然没有前两点重要,但仍很有价值,能为相关数据的使用提供方便。利用数据整形可以为嵌套的数据集生成通用的例程。示例站点上就有这样的一个例程。
在下面两章以及本书的其余部分将看到相关的其他知识。在后续章节将详细探讨 We b服务器与浏览器之间的交互。毕竟,仅有数据是不行的,还要让人们看见这些数据。
298计计 ASP 3 高级编程 下载