四、处理查询结果   查询结果记录发送到客户端之后,客户端应用程序可能需要相当可观的时间去处理结果集。每一种体系(客户机/服务器,多层体系中的中间层,以及ASP)都为优化这个阶段的代码提供了相应的技术。下面是几个能够显著改善性能的技巧。   我在代码中看到最多的错误之一是:在引用Recordset Field.Value的时候,使用延迟绑定(Late Binding)。由于代码需要频繁地引用Value属性,而且通常要引用的Field对象有很多,本文前面提到的倍数因子将起到重要影响——因此,所有这里介绍的技巧能够显著地改善性能。一些开发者使用延迟绑定技术的原因在于,他们想要明确地标识出SELECT语句选择了哪些行。为了这个目标,许多人使用了用引号包围字符串的做法。例如,为了引用记录集RS字段集合中的“Cows”字段,你可能使用:    
RS("Cows") 或者: RS.Fields("Cows").Value
 
  后面这种方法显式地引用了记录集内Fields集合中指定成员的Value属性。这种方法要稍微快一点,而且当你把这些代码向Visual Basic.NET迁移时,它的向上兼容性也要好一些。上述方法的一种变化是使用感叹号(!)操作符:    
RS!Cows
 
  与先行绑定(Early-Binding)相比,采用上述方法时COM进行解析的时间要长得多,这是因为它们强制COM在运行时(而不是在编译时)解析对Value属性的引用,每一次对该对象的引用都要求有一系列类似的、后台进行的查找过程。   然而,使用延迟绑定时,不存在代码引用的是哪一个列这类问题。如果你完全按照下列方式编写代码:    
RS(0) ' 指向第一个列(Fields集合的成员)
 
  这时,COM能够在编译时解析Value属性地址,代码的运行速度将加快。但是,只有那些了解查询所返回的列以及返回次序的人能够理解这行代码到底指向了哪一个列。如果开发者不具备控制查询数据源的权限(这是很常见的情况),这种方法可能带来问题。为了确定RS(0)引用了哪一个SELECT列,你必须找出生成该Recordset的是哪一个SELECT语句、搞清楚SELECT语句所返回的各个列。   然而,有几种技术允许你既能够实现快速地运行时引用,同时保证代码的可读性。其中一种方法的要求如下:开发者必须创建一个查询所返回列的枚举列表。如果查询被改变,比如返回更多的列,或者列的次序发生变化,开发者必须修改和重新部署枚举列表。遗憾的是,要保持枚举列表与查询的匹配,对于管理者来说是一个有些困难的任务。例如,为了快速、明白地标识出ADO代码引用的是哪一个列,你可以结合下面的SELECT语句和枚举列表:    
SELECT CatName, CatType, CatSize from Cats Where... Enum enuCatsQuery CatName CatType CatSize End Enum
 
  注意,SELECT语句返回的列与枚举列表中声明的列完全匹配。此后,当你需要引用Recordset的Fields集合时,可以使用下面的代码:    
StrMyName = Rs(enuCatsQuery.CatName)
 
  按照这种方法,代码不仅具有较好的可读性,而且它仍旧是编译时绑定,代码的运行速度明显加快。   然而,要避免延迟绑定,你还可以使用另外一种方法。[email protected]列表服务上一场长时间的讨论得出了一种我称之为预先绑定(Prebinding)的方法,它结合了两种技术。当你只需引用Field对象一次时,这种技术没有什么帮助;但在客户机/服务器应用中,预先绑定方法非常理想。使用这种方法时,你要创建多个独立的、命名的Field对象,并把这些对象设置为Recordset对象Fields集合中的成员。编写代码的时候,你首先要为每一个想要使用的字段创建一个命名的Field对象,例如:    
Dim fldName as ADODB.Field Dim fldType as ADODB.Field Dim fldSize as ADODB.Field
 
  创建这些Field对象需要一定的开销。然而,你应该估量一下,这是一次性的开销,但它却能够戏剧性地改善性能。   打开Recordset之后,你只需一次性地把这些命名的Field对象设置为SELECT查询选择出来的列:    
If fldName is Nothing then Set fldName = RS!CatName Set fldType = RS!CatType Set fldSize = RS!CatSize End if
 
  你可以在这里用引号包围字符串的方法引用列,甚至也可以使用感叹号操作符。由于这里的代码只运行一次,不论使用什么方法,性能的差异都不大。接下来,当你需要引用Field对象(查询之后)时,只需使用预先绑定的变量即可:    
strName = fldName strType = fldType strSize = fldSize
 
  这种预先绑定方法的性能甚至比序数引用方式(例如RS(0))都要好。 五、客户机/服务器、中间层和ASP策略   在编写代码的时候,你还必须考虑到其他一些影响性能的因素。其中一些因素与ADO没有什么关系——它们与COM有关。Microsoft最近的一份白皮书指出,在Windows 2000 ASP页面中执行ADO操作(连接,查询,处理)要比调用COM组件执行同样的代码更快。这个结论并不令人奇怪:当我们从VB调用一个外部COM组件(处于当前进程之外的一些代码),访问COM组件以及把控制传递给它时在后台进行的操作复杂得出奇,而且速度很慢。虽然我们没有必要刻意避免调用COM组件去运行ADO代码。但是,我们不应该简单地把多个独立的ADO操作封装成大量的小型COM组件,然后在需要的时候每次都去调用它们。相反,我们应该尽量把全部逻辑封装到一个COM组件里面,使得程序只需一次调用,COM就能够完成大多数(如果不是全部)操作。我相信,你已经发现运行二进制形式的(例如COM组件)ADO代码要比运行ASP之类的解释执行代码要快。因此,你应该寻找一些方法,减少进入COM组件和从COM组件返回所需要的昂贵开销。   如果你离不开Command对象,或者不能预先绑定Field对象并在必要时重用,那么你应该考虑避免多余的对象创建操作的技术。在这种情况下,把存储过程作为Connection对象的方法调用有着更重要的意义。另外,用先行绑定的方式引用Field属性也有助于改善性能。记住操作完成后必须进行的清理工作:关闭Connection和Recordset对象,然后把它们设置成Nothing。   为了让代码和代码编写者都表现出最好的性能,请记住以下基本规则:利用连接池和异步连接;减少ADO代码和数据库服务器通信的次数;选用一种COM-先行绑定技术;除非必要,尽量避免使用代价昂贵的ADO对象,例如Recordset和Command对象;如有可能,用Return Status和Output参数替代记录集。尽可能地提高查询的效率,如有可能,不要忘了利用存储过程。详细地告诉ADO你想要它做些什么,避免让ADO猜测你的意图——显式地指定ADO CommandType,使用adExecuteNoRecords之类的选项。   对于本文所介绍的所有技巧和其他文章提出的编程忠告,我建议你以审视的目光看待它们。我们所做的工作、所编写的代码、所构造的系统,都属于非常复杂的东西,许多不断变化的因素影响着它们。理解了这一点之后,如果你对本文所讨论的某一项技术感兴趣,可以先进行一下试验。如果它确实有效,那么就正式实现它,然后再进行测试。如果这时它仍旧有效,那么恭喜你。如果它不再有效,你得看看是否违反了应用该技术的必要条件。

解决方案 »

  1.   

    简单地说比如:Sql Server       
    客户端用vb写一个存储过程,处理SQL SERVER 上的数据
    SQL SERVER接受指令后,在服务器上进行计算,将结果返回客户端,返回的结果可以是一个记录集或变量以前我做计算用临时表(用完后需Drop Table,如果不删除就有问题),现在改用存储过程,性能提高.建议大家以后都用存储过程
    Good Luck!!!!!!!!!!!!!
      

  2.   

    SQL SERVER7.0也缓存客户端传来的一般SQL语句的查询计划;当有相同的SQL语句传来时,SQL SERVER会查找保存在缓存中的中的执行计划,如果匹配,则重用这一计划。这样显然提高性能。
    为了做到这一点,查询语句应遵守两条原则:
    1、保证查询语句中的每个对象都可用(必须)
    如:select * from horses order by horsename 则不会被重用,应该为
    select * from 数据库名.dbo.horses order by horsename,如果表的拥有者即是数据库的拥有者(缺省值),则可以改为:select * from 数据库名..horses order by horsename 
    2、查询参数化(建议):
    如:strSQL="select * from horses where horsename like '" & strName
    & "' order by horsename"
    改造为strSQL="select * from 数据库名..horses where hosrsename like ? order by horsename"
    然后使用Command对象传递参数:
    dim cmd as Command
    .......
    cmd.commandtext=strSQL
    cmd.parameters(0)=strName这两种方法可以提高重用的概率。