词条 | 远程数据对象 |
释义 | § 简介 远程数据对象 RDO (Remote Data Objects) 是一个到 ODBC 的、面向对象的数据访问接口,它同易于使用的 DAO style组合在一起,提供了一个接口,形式上展示出所有 ODBC 的底层功能和灵活性。尽管 RDO 在很好地访问 Jet 或 ISAM 数据库方面受到限制,而且它只能通过现存的 ODBC 驱动程序来访问关系数据库。但是,RDO 已被证明是许多 SQL Server、Oracle 以及其他大型关系数据库开发者经常选用的最佳接口。RDO 提供了用来访问存储过程和复杂结果集的更多和更复杂的对象、属性,以及方法。 RDO(Remote Data Objects)处理一组对象以完成远程资料的存取,它是在ODBC API和驱动程序之上的一个可程序的薄层(thin code layer),用以建立资料结果集(result set)和光标,以及用最小的工作站资源执行复杂的程序。 注意RDO祇能在32位的操作系统上执行(如Windows95或NT) 主题 l RDO的特性 介绍RDO的重要特性 l 以RDO写程序 介绍RDO对象模型 l 和Jet/DAO之间的比较 RDO的特性如何与DAO对应 l 初始化rdoEngine对象 rdoEngine对象的概观与如何去设定它的属性 l 建立rdoEnvironment对象 rdoEnvironment对象的概观以及如何应用它的属性来做数据存取 l 使用RDO来执行查询 如何建立以及执行有参数的查询 l 使用RDO来执行stored procedure 介绍如何与有特殊需求的store procedure一起运作 l 建立RDO光标 介绍如何建立RDO光标 l 使用RDO结果集 介绍如何使用从查询传回的资料以及参数 l 使用ODBC API函数 介绍RDO支持的ODBC handles l 使用RemoteData控件(control) 如何用RemoteData控件处理数据 RDO的特性和客户端/伺服端的设计目标 使用RDO时,程序可以直接存取ODBC的资料源而不需要本地端的查询处理。这会在存取远程的数据库引擎时大大地提高效率和弹性。 使用RDO可以得到下列的好处。特别是在客户端/伺服端架构 l 提高自远程ODBC资料源存取资料的效率。 l 建立简单而无光标的结果集,或是较为复杂的光标。 l 执行查询以及同时运作多结果集:经由一个单一的查询可以传回多个结果集,这可以提高查询处理器和系统资源的使用效率。例如可以使用一个查询来提供多个显示资料对象的资料(如data-driven的list box或查询一起运作,可以正确的设定scroll bar和progress status bars。 l 以单一批次(batch)执行多个action查询:在很多状况下可能是以单一的SQL述句来执行多个新增(INSERT)、删除(DELETE)和修改(UPDATE)的动作,这可以减少网络和远程处理的负载,同时让处理交易(transaction)更方便。 l 可以使用参数输入stored procedure,以及传回结果:从stored procedure输出参数是从Oricle的stored procedure上获得运算结果的唯一来源。这会在单一查询以及许多管理的运作上有用。在很多状况下,必须藉由stored procedure传回的结果来判断所执行的运作是否成功。RDO经由rdoParameter对象来处理这些输入、输出的参数。 l 限制处理以及传回的记录数:在某些状况下,使用者所选的记录数目可能超过实际运作的量,RDO可以设定经查询传回的记录数目量。以这种方式可以预测响应时间,也更容易管理工作站和服务器的资源。以同样的机制可以限制修改查询(data-modification query)所能影响记录数的量。 l 利用伺服端光标:一些伺服端数据库引擎也提供了光标的运作,在某些状况下使用伺服端光标可以减轻网络负荷以及工作站的资源以提升效率。 l 监控远程资料源所产生的讯息或错误但不妨碍正在执行的查询。 l 执行异步查询:能够使用同步、异步事件驱动((event-driven)异步等处理,使程序不会在执行大量查询时停滞。且不需要使用Polling机制。在异步查询期间,Connection会一直保留着。 l 在初始设定的查询截止时间到时,仍可继续:当查询的运作时间超过QueryTimeout属性时,RDO允许继续另一个查询等待时间,而不直接停止查询。 l 提供改良的多形(polymorphism)和“free-standing”的对象产生方式:RDO支持以DIM宣告且产生对象的方式,这种free-standing的对象可以被连结到其它的对象以执行运作。例如,可以独立的产生rdoQuery和rdoConnection对象,而后联合在一起运作。 l 提供分离的结果集:RDO允许产生静态的读写光标而后中断与远方服务器的连结。在rdoResultset对象中的资料仍可存取,一但以另一个rdoConnection对象再连结上远程伺服机时,可以以BatchUpdate方法以完成这种离线(offline)的改变。 l 产生和管理开放式的批次更新:当ODBC光标链接库提供开放式更新时,它是以一笔接着一笔的方式而非批次更新。这种做法需要较多的网络和服务器的频宽。RDO提供新的客户端批次(Client Batch)光标,将一群被新增,修改和删除的记录集合起来。这种方式祇需要与伺服端连结一次,因此可以提升效率 l 让Stored Procedures使用更容易:RDO允许将可输入参数的查询和stored procedure当成rdoConnection对象的方法。可以像使用VB函数传入参数的方式一样传入参数而不需要rdoParameter对象。 l 提供之下的ODBC handles:当需要更直接,更有弹性的资料存取时,RDO提供直接存取ODBC环境(environment),connection和statement 等handles。 l 减少内存使用 以RDO写作程序【2】 RDO对象和集合提供了写作程序和维护远程ODBC资料的的架构。对象和集合有描述数据库特性的属性和操作数据库的方法。使用这种包含子对象的架构可以建构出对象与对象或对象与集合等等的关系,而这些关系表现了数据库系统的逻辑结构。 除了rdoEngine对象外,其余所有的对象都是由相关的集合在维护。当RDO在第一次存取而被初始化时,会自动产生rdoEngine对象以及内含的rdoEnvironments(0)对象。 RDO的程序写作结构与DAO相似,但更注重在stored procedure的操作和传回的资料集,相对的对ISAM资料的存取则较不重视。 下表为各对象的简介 RDO对象描述 rdoEngine最基础的对象,当第一次存取RDO对象时会自动产生。 rdoError处理ODBC错误以及RDO所产生的讯息。也是自动产生的。 rdoEnvironment对不同使用者定义逻辑上的connections和transaction的范围,包含被开启的或未开启但拥有的connections,提供仿真transaction的机制,提供一个security context来执行data manipulation language(DML)。rdoEnvironment(0)会自动产生。 rdoConnection代表一个连到远程资料来源的开启的connection,或是一个拥有但未开启的对象,该对象可以在稍后开启。 rdoTable代表一个储存着的基本资料表(base table)的定义或是一个view rdoResultset代表执行一个查询后所传回的记录 rdoColumn代表拥有一般的资料型态以及属性的一个数据域位 rdoQuery一个可以拥有参数的SQL查询 rdoParameter代表输出入的参数,关联于rdoQuery对象 注意:要支持分布式交易(distributed transaction)需要搭配SQL Server6.5版以及Distributed Transction Coordinator。 注意:RDO 1.0版提供的rdoPreparedStatement对象和rdoPreparedStatements集合,在RDO 2.0版仍然支持,但是是为了向前兼容,所以在RDO 2.0版后最好改用rdoQuery对象和rdoQueries集合。 产生远程资料对象 RDO对象可以经由父对象产生或经由Dim述句。如果是经由Dim as New述句产生的RDO对象不会被加入到相关的集合中。下表列出如何产生主要的RDO对象 产生远程资料对象 RDO对象可以经由父对象产生或经由Dim述句。如果是经由Dim as New述句产生的RDO对象不会被加入到相关的集合中。下表列出如何产生主要的RDO对象 RDO对象产生方式 rdoEngine自动产生 rdoEnvironment第一个instance自动产生rdoEngine.rdoCreateEnvironment rdoConnectionrdoEnvironment(x).OpenConnection(也可以用RemoteData控件产生)Dim MyCn as New rdoConnection rdoQueryrdoConnections(x).CreateQueryDim MyQry as New rdoQuery rdoResultsetrdoQuery(x).OpenResultsetrdoConnection(x).OpenResultset rdoParameter会为有参数的查询自动产生 rdoTable当被参照时自动产生 rdoError当ODBC错误发生或有讯息时自动产生 § RDO与Jet/DAO的比较 虽然RDO与DAO大致相似,但RDO是专门操作关系型数据库的对象,且没有自己的查询处理(processor),它完全靠资料来源处理查询并产生结果集以下是RDO与DAO/Jet 的比较表 RDO对象相对的DAO/Jet对象 rdoEngineDBEngineDBEngeine rdoErrorErrorError rdoEnvironmentWorkspaceWorkspace rdoConnectionDatabaseConnection rdoTableTableDef不支持 不支持索引不支持 rdoResultsetRecordsetRecordset 不支持 Table-type 不支持 Keyset-type Dynaset-type Dynaset-type Static-type(r/w) Snapshot-type(r/o) Snapshot-type(r/o) Dynamic-type 不支持 Dynamic-type Forward-only-type Forward-only-type Forward-only-type (cursorless) 不支持 (cursorless) rdoColumnFieldField rdoQueryQueryDefQueryDef rdoParameterParameterParameter 不支持Relation不支持 不支持Group不支持 不支持User不支持一些DAO的对象、方法、和属性是设计来操作ISAM架构下的Jet数据库。例如可以利用索引(Index)对象和Seek方法来处理ISAM的索引以及找寻记录。但是因为RDO所使用的后端数据库处理索引的方式完全不同,所以RDO并不需要去支持这些索引的运作。DAO也支持建造和修改数据库架构(schema),参照的完整性以及安全上的维护。这些对RDO来说都是后端数据库服务器在维护的,所以本身并不提供相关的对象或方法。但仍然可以利用RDO的make-table查询;以标准的SQL述句来执行动作查询(action queries)以产生、修改或删除数据库和资料表。同时也可以以复杂的stored procedures还处理数据库的架构和执行为或的工作,这些是DAO做不到的。 RDO的集合管理 RDO使用集合来管理除了rdoEngine对象外的每一个对象。由于VB5.0支持独立产生某一个对象,所以一些独立(stand-alone)的对象如rdoConnection和rdoQuery可以个别的产生,而不会自动地加到父对象的集合内(虽然大部分的对象会)。独立的对象可以以add方法加入集合,也可以以Remove方法加以移除。当使用CreateQuery方法时,会自动产生rdoParameters集合。但是如果ODBC接口因为某种理由无法解析传来的SQL,则用来管理查询的参数的对象将不会产生,所以若这时参照rdoParameters集合会产生可捕捉的错误。另外若ODBC驱动程序不支持SQLNumParams函数,或呼叫这个函数时,传回否,也会造成无法产生rdoParameters集合。下表列出RDO管理的集合 RDO集合说明 rdoErrors内含所有RDO一次运作后所产生的rdoError对象。 rdoEnvironments内含rdoEngien之下,所有运作中(active)的rdoEnvironment对象,rdoEnvironments(0)会被自动产生。 rdoConnections内含所有在rdoEnvironment对象下开启的rdoConnection对象,或是以Add方法加入的rdoConnection对象。 rdoTables内含所有在数据库中预存的rdoTable对象。 rdoResultsets内含所有在rdoConnection中开启的rdoResultset对象。 rdoColumns内含所有在rdoResultset或rdoTable对象中的rdoColumn对象。 rdoQueries内含所有以rdoConnection对象的CreateQuery函式而自动加入的,或以Add函式加入。 rdoParameters内含所有经由解析成功的SQL述句所产生的rdoParameter对象。 RDO2.0集合 若设定一个已经赋予对象的对象变量一个新产生的对象,RDO会自动关掉该变量原内含旧有的对象。 Dim cnn As rdoConnection Dim strConnect As String strConnect = "UID=;PWD;" Set cnn = rdoEnvironments(0).OpenConnection("Public", rdDriverNoPrompt, False, strConnect) Set cnn = rdoEnvironments(0).OpenConnection("Public1", rdDriverNoPrompt, False, strConnect)若程序代码如上,则Connection对象Public会自动关闭并移除,再将Connection对象Public1赋予给cnn变量。 § 初始化rdoEngine对象 rdoEngine对象是所有RDO对象的最上层对象。它不能被重复产生,以及使用集合来包含。它可以让好几个应用程序共享,但会为每一个参与共享的应用程序产生私有资料区,而不会让应用程序彼此之间互相干扰。 初始化rdoEngine的内定值 当产生新的rdoEnvironment、rdoConnection或rdoResult对象,可以透过rdoCreateEnvironment或OpenResult方法内的参数来设定,若没有设定可以经由rdoEngine的内定属性来设定。各属性如下表 属性定义内定值 rdoDefaultCursorDriver光标链接库(ODBC、客户端批次、伺服端光标,或是没有)rdUseIfNeeded(伺服端光标) rdoDefaultPassword使用者密码“”(空字符串) rdoDefaultUser使用者帐号“”(空字符串) rdoDefaultErrorThreshold发生多严重的错误才停止-1(不致能disabled) rdoDefaultLoginTimeout建立connection等多久没响应,才放弃15秒 捕捉InfoMessage事件(Event) rdoEngine在ODBC接口从远程受到讯息时会触发InfoMessage事件。当RDO收到传回值SQL_SUCCESS_WITH_INFO以及处理完rdoErrors集合后,就会触发这的事件。虽然一个RDO方法可能产生很多讯息,但RDO祇会触发一次这个事件。它会把所有传回的讯息都放入集合后再触发事件。藉由rdoErrors集合可以判断该做啥。例如执行了一个统计的查询后,这个讯息会经由rdoErrors集合传回,而应用程序可以经由InfoMessage事件的触发而知道。 处理RDO的错误和讯息 每当ODBC驱动程序管理员在处理RDO需求时,Visual Basic、ODBC接口、或远程服务器都可能发生错误。这些错误轻重不一,严重的可能导致强制停掉或放弃该次查询。Visaul Basic产生的错误都可以经由On Error述句捕捉,若错误经由ODBC中层或后端服务器产生则会放到rdoErrors集合中。当RDO呼叫ODBC而传回值为SQL_ERROR时,VB也会产生可捕捉的错误。因此应用程序代码可以利用rdoErrors中各自独立的对象决定产生错误的原因以及处理的方式。当传回值是SQL_SUCCESS_WITH_INFO时,则不回产生可捕捉的错误,但传回的讯息一样放在rdoErrors集合中。当然也有可能在RDO执行的过程中产生错误,而非ODBC传回错误,这一样会引发VB的可捕捉错误。例如以CreateQuery方法产生查询的述句中不必用到参数,但之后却使用rdoParameters集合,这会引发RDO的错误。一旦参考过rdoErrors集合后,可以以Clear方法清掉其中的内容。这可以防止经常检查rdoErrors确定当下状况的程序结构产生混淆,将以前发生的错误当成现下动作的错误。当执行完查询后,不管查询成功或失败,QueryComplete事件都会被触发。这时可以检查布尔旗标ErrorOccurred以确定查询成功或失败。同样地,再建立连结时也可以ErrorOccurred来检查成功与否。 § 产生rdoEnvironment对象 在大部分的状况下并不需要产生额外的rdoEnvironment对象,内定的rdoEnvironments(0)应该够用。但若应用程序需要更多的交易处理,或不同的帐号密码环境,那就需要以rdoCreateEnvironment方法给予不同的使用者名称、密码来产生另外一个rdoEnvironment对象。这时要给予一个唯一的rdoEnvironment名称,若与已经产生的相同,则会有可捕捉的错误。内定的rdoEnvironments(0)的名称为”Default_Environment”,使用者名称及密码都是空字符串。若提供了唯一的名子,新产生的rdoEnvironment对象会自动加入rdoEnvironments集合。反之,若以空字符串当name参数的值,则新产生的对象不会加入。 快取(cached)登入信息 若在使用OpenConnection方法时,没有在connect参数设定使用者帐号密码,或是使用RemoteData控件,但未在Connect内指定,则RDO会用rdoEnvrionment的帐号密码建立连结。如下例,会用使用者帐号(Fred)和密码(Blond)在rdoEnvironment对象En内建立连结Dim En As rdoEnvironmentSet En = rdoCreateEnvironment(“”,”Fred”,”Blond”) 使用rdoEnvironment对象管理交易(Transaction) rdoEnvironment支持BeginTrans、CommitTrans和RollbackTrans方法来管理ODBC分布式交易(distributed transactions)。但这还要靠Distributed Transaction Coordinator(DTC)来支持。现下只有MS SQL Server 6.5支持DTC。所有在rdoEnvironment内的rdoConnections集合下的Connection对象都可以参与一个分布式交易。 The Microsoft Distributed Transaction Coordinate(MS DTC) MS SQL Server的新功能是藉由DTC管理协调跨越Windows NT或Windows 95平台上的分布式交易。SQL Server可以做到l l 在两台以上的SQL Server 6.5完成更新l l 可以参与监控经由X/Open DTP XA控制的交易,如Transarc的Encina、AT&T Global Information Solutions Company的Top End和Tuxedo。l l 提供容易使用的使用者接口管理分布式交易。也可以在SQL Server提供的Enterprise Manager的Server选项下使用Distributed Transaction Coordinator的选项功能。MS DTC是专门处理在网络上的分布式交易,完全与MS SQL Server 6.5整合。 RDO与DTC无关的交易管理 若在没有DTC,或当下的ODBC驱动程序不支持分布式交易的状况下依然要让RDO管理跨connection的交易,这会让这些交易顺序性地(serialized)发生,但不保证完整性(atomic),亦即,一个rdoConnection内的交易可能会完成,但另外一个可能失败了。在这时,完成的那个不会回复。因为在应用程序内,rdoEnvironment决定交易的范围;所以若在该对象内下完成交易的命令,则所有其下所开启rdoConnection的数据库若有未完成的交易都会一并做完成的动作。但其间并没有支持两阶段交易(two-phase commit)这种协议。注意:ODBC的交易模型并不支持巢状交易(nested transaction),所以不能在前一个BeginTrans还未与CommitTrans或RollbackTrans成对之前再使用一个BeginTrans方法。但若ODBC的资料源本身如SQL Server支持巢状交易,则可以透过SQL述句,叫伺服端进行巢状交易。当使用ODBC交易时,一个开启的连结并不会影响另一个连结,即使这两个连结是连到同一个数据库的同一个资料表。 捕捉rdoEnvironment交易事件 当rdoEnvironment完成一笔交易后会引发一个事件,可以利用这个事件来管理其它的交易。 在rdoEnvironment对象管理Connections 利用rdoEnvironment对象的OpenConnection方法可以新增到资料来源的连结,并产生rdoConnection对象来参照并管理这些连结。在rdoEnvironment环境内,可以开启多条连结,每一条连不一样的数据库,管理交易并以使用者的帐号密码建立安全机制。例如l l 产生一个以名称、使用者帐号和密码来开启一个有帐号密码保护的环境。在这个环境下可以开启多条连结与管理多个ODBC交易。l l 使用OpenConnection方法在rdoEnvironment之内建立一个以上的连结l l 在rdoEnvironment内使用BeginTrans,CommitTrans和RollbackTrans方法来管理ODBC交易。l l 使用多个rdoEnvironment对象来建立多个、同时、独立和重叠的交易。l l 使用Dim X As New rdoConnection来产生一个独立(stand-alone)rdoConnection对象,接着使用Add方法加入到rdoEnvironment对象内的rdoConnections集合。l l 使用Remove方法将特定的rdoConneciton对象从他的父集合rdoConnections中移除。l l 使用Close方法来结束环境和连结注意:rdoConnection对象不一定要属于某个rdoEnvironment,它可以独立产生。 § 建立RDO连结 在参照一个远程数据库内的资料之前,要先建立一条连结到资料源。这个资料源可以是远程的数据库服务器,像MS SQL Server或Oracle,或其它支持适合的ODBC驱动程序的数据库。RDO建立连结有好几种方法,与DAO不同的是,它并不为应用程序管理这些连结;仅仅收集要传给SQLDriverConnect函数的参数,和使用SQLDisconnect函数来关掉连结。RDO并不快取这些资料以分享给使用相同DSN来建立连结的后来者。当使用RDO的Close方法来关掉连结时,它立刻就会被关掉。可以以下的方法开启连结l l 使用RemoteData控件自己的Connection属性来建立连结对象rdoConnectionl l 宣告一个rdoConnection对象,再以rdoEnvironment对象的OpenConnection方法开启后赋予l l 用Dim 变量名 As New语法产生一个独立的rdoConnction,设定它的属性后再以EstablishConneciton方法建立连结l l 无论在独立的rdoConnection或是使用过Close方法的rdoConnection都可以用EstablishConnection方法建立连结在不同的情况下,可以选择不同的方法。例如要对一些远程数据库完成象,再透过rdoQuery对象的ActiveConnection属性将rdoConnection对象设定(Assign)给这些rdoQuery。在大部分状况下,RemoteData控件建立连结较为简单。以上的这些方法都是要与资料源如SQL Server,或Oracle;建立一个实体的连结(link)。要建立这个连结需要提供资料源的网络位置、驱动程序型态以及一些用来确认使用者身份的选择性参数。一但建立了连结,可以用它来l l 使用OpenResult方法来执行一个查询以传回一或多个结果集。l l 使用Execute方法以执行一个动作查询(action query)l l 产生一个rdoQuery对象,好接着执行参数查询或预存程序。l l 定义执行一个以上预存程序的查询来当成rdoConnection的方法。l l 利用rdoResultset对象的ActiveConnection属性连结rdoConnection对象l l 使用Add方法将rdoConnection加到rdoConnections集合l l 使用Remove方法将rdoConnection从rdoConnections集合移除 提供RDO的连结字符串(Connection Strings) 为了要提供ODBC驱动程序管理员要使用哪一个驱动程序,以及哪一个资料源,程序代码必须以特定格式的连结字符串或RemoteData控件的属性来设定。在大部分情况下,连结字符串都是告诉RDO哪个使用者,要使用哪个伺服端上的哪个数据库。连结字符串以一连串的分号来隔开各个参数,格式是由ODBC接口定义的,这其中包含了指定ODBC的驱动程序。每一个ODBC的驱动程序可能各有不同的参数,需参照各自的说明。这些参数都会加上rdoEnvironment对象的hEnv后一起传给ODBC API的SQLDriverConnect函式。注意:若是转换既有的DAO或ODBCDirect程序,则ODBC连结字符串的“ODBC;”要去除。另外,不支持LOGINTIMEOUT参数,改以rdoEnvironment对象的LoginTimeout属性取代。通常连结字符串的参数选项如下表 ODBC联机字符串参数参数定义 DSN=注册ODBC资料来源名称(Data Source Name DSN),如果用了驱动程序参数,则DSN就不要用。 UID=在伺服端建立的使用者帐号,以SQL Server来说就是登入的帐号 PWD=对应登入帐号的密码。 DATABASE=需要的内定数据库(选项)。 SERVER=资料来源服务器的网络计算机名称,若操作系统为MS Windows NT,且数据库服务器在本机,则网络计算机名称可用(local)。 DRIVER=资料来源的服务器驱动程序名称,以MS SQL Server为例,这里用的是{SQL Server}。若使用了DRIVER参数,就不需要使用DSN。 APP=应用程序的名称(选项)。 WSID=工作站ID,通常是应用程序所在工作站的网络名称。(选项)。 LANGUAGE=SQL Server所使用的国家语言。注意:不同的数据库服务器会有不同的参数选项。 连结字符串所没有的 注意以下是连结字符串所没有的 选项内定行为 Network Address以服务器网络名称代替,所以不需要。 Network Protocol若是MS SQL Server,内定是Names Pipes。 OEMTOANSI switch内定是“Off”。 使用Trusted Connection Option内定是“Off”。 为Prepared Statement产生预存程序内定是“Off”。上表所说的,以及一些其它某些驱动程序所特有的属性要在Windows的控制台内设定。若不用DSN,则上述的这些属性就会用内定值。 提供使用者帐号和密码 若要使用ODBC资料来源,大部分都要提供使用者帐号和密码。藉由设定OpenConnection或EstablishConnection方法内的参数,可以设定是否要使用者当场输入帐号和密码。但这可能会增加替每一个使用者设定权限的麻烦,与安全漏洞。可以改成应用程序直接以某几种的帐号密码登入。若要搭配NT网络系统的DOMAIN安全管理,则上述两个方法的连结字符串内的UID和PWD要保持空白。写法如下Conn$=”DSN=MyRemote;UID=;PWD=;” 设定内定的数据库 若rdoConnection对象在建立之初,未设定DATABASE参数,则内定使用的数据库可能是注册的DSN内定义的数据库;或是伺服端系统管理员定义的数据库。但内定的数据库可以藉由执行Transaction SQL的“USE 数据库名”予以更改。范例如下cn.Execute “USE Pubs” 传递连结字符串给RDO或RemoteData控件 可以以下列方式传递连结字符串给RDO或RemoteData控件l l 执行OpenDatabase方法时,设定Connect参数。l l 设定RemoteData控件的Connect属性。l l 当rdoConnection使用EstablishConnection方法时,设定Connect属性若使用RemoteData控件,则有一些属性都与连结有关,但如果Connect属性内有设,其它相关的属性也有设定的下,Connect内的属性会盖过其它的属性。 使用已经注册好的DSN 最简单的建立连结方法就是使用已经注册好的DSN来引用储存在DSN内的相关伺服端设定。因为DSN并未包含使用者与应用程序的相关信息,所以这部分需在连结字符串,或是利用使用者交谈窗来设定。设定一个DSN要用窗口操作系统的控制台,或是rdoRegisterDataSource方法。但较建议使用控制台内的ODBC图标。可以在以下方法中用到DSNl l OpenConnection方法的dsName参数。l l RemoteData控件的Database属性。l l OpenConnection方法中以连结字符串设定Connect参数。l l 在rdoConnect对象设定Connect属性且使用EstablishConnection方法。l l RemoteData控件的Connect属性程序范例如下 Dim env As rdoEnvironment Dim cnn As rdoConnection Dim strAttribs As String strAttribs = "Description= SQL Server on server UI01" & _ Chr$(13) & "OemToAnsi=No" & Chr$(13) & "SERVER=UI01" & _ Chr$(13) & "DATABASE=Pubs" rdoEngine.rdoRegisterDataSource "NewDSNRegByVB", "SQL Server", True, strAttribs Set env = rdoEngine.rdoEnvironments(0) Set cnn = env.OpenConnection("NewDSNRegByVB", rdDriverNoPrompt, "UID=sa;PWD=;") 产生不用DSN的RDO连结 也可以产生不用DSN的RDO连结,这时所需要的设定都需要在使用OpenConnection方法时透过Connect参数来设定,这有以下的好处l l 客户端应用程序可以自由建立连结l l 不需要先参照系统注册(registry)内的DSN设定,所以速度较快。l l 对于特定的伺服端可以有更多的控制,以增进应用程序与伺服端的安全。若产生不用DSN的RDO连结,则服务器和驱动程序的名子都要包括在连结字符串内,且OEMTOANSI(Off)、NETWORK(named pipes)等属性祇能用内定值,因为这些属性祇能在建立DSN时设定。若选择不用DSN的RDO连结,则OpenConnection方法的dsName参数要以空字符串输入,或以空字符串设定RemodeData控件的Database属性。这告诉ODBC驱动程序要建立不用DSN的RDO连结。除此之外,连结字符串参数的顺序设定上,DSN的顺序一定在伺服端和DRIVER 之后。范例如下 Dim env As rdoEnvironment Dim cnn As rdoConnection Dim strConnect As String strConnect = "UID=sa;PWD=;DATABASE=Pubs;" & _ "SERVER=UI01;DRIVER={SQL SERVER};DSN="";" Set env = rdoEngine.rdoEnvironments(0) Set cnn = env.OpenConnection(dsname:="", prompt:=rdDriverNoPrompt, Connect:=strConnect) 设定rdoConnection选项 在使用OpenConnection或EstablishConnection方法前可以先设定许多会影响建立连结的方式与查询使用该连结的方式。通常来说,这些选项是由rdoEnvironment对象的属性来设定,但若建立的是独立的rdoConnection,则必须在建立之前先设定下表的属性。 rdoConnection属性内定值设定 LoginTimeout15在放弃建立连结前要等多少秒 CursorDriverrdUseIfNeeded使用该rdoConnection对象的查询将会用的光标形式 Connect“”用来产生连结的ODBC参数—连结字符串 Name“”连结时用的ODBC DSN 使用rdoConnection对象的事件(Event) rdoConnection可以触发一连串的事件,以用来管理连结和使用该连结的查询。下表示会触发的事件 rdoConnection事件何时触发 BeforeConnect在呼叫ODBC SQLDriverConnect函数之前 Connect在建立连结运作完成后—不一定成功或失败 Disconnect在连结被断掉时(disconnect) QueryComplete在异步(asychronous)查询结束时 QueryTimeout在查询所使用的时间超过rdoConneciton对象的QueryTimeout属性。 WillExecute在RDO准备执行一个查询时。在rdoConnection执行一个相关的查询时,QueryComplete、QueryTimeout和WillExecute事件都会被触发。这包括使用OpenResultset或Execute方法,或经由相关的rdoQuery执行相关的查询。 Remote Data Objects是架在ODBC API上的thin object-model层。它的内容精简(约250K),运作的好坏还要看在它之下的ODBC Driver以及后端数据库写得如何。RDO也提供之下ODBC的Handle,所以大部分的ODBC API可以直接执行。 使用BeforeConnect事件处理 在产生连结之前会触发的事件,可以利用这个机会更改传到ODBC SQLDriverConnect函式的连结字符串。 使用Connect事件处理 在连结建立好之后,这个事件就会被触发。在建立连结时,若很需要时间,特别是针对广域网络(wide area net WAN)的状况,若在使用OpenConnection或EstablishConnection方法时,以rdAsyncEnable选项建立异步连结,就可以这个事件取代polling rdoConnection对象的StillConnecting属性。若建立连结有错误产生则可以参考Connect事件传入的ErrorOccurred参数是否为True 使用QueryComplete事件处理 当使用异步查询时,可以藉由QueryComplete事件的触发来决定是否已经完成查询。这个事件是属于Connection对象的,所以用该Connection对象的所有查询所触发的QueryComplete事件是同一个。大部分的ODBC驱动程序都不支持在同一时间;一个Connection执行多个查询。但若支持的话,则要凭QueryComplete事件传回的Query参数,确定是哪一个查询对象完成。若执行查询有问题,则ErrorOccurred参数会传回True 使用QueryTimeout事件处理 某些查询可能会耗掉很长的时间,可以藉由设定QueryTimeout属性设定执行查询多少秒后,若还未完成就触发QueryTimeout事件;内定是30秒。若查询触发了QueryTimeout事件,可以藉由设定该事件的Cancel参数来决定是否继续,若输入False则RDO会继续该查询直到下一个时间间隔用完或查询结束。若输入True,则直接停掉该执行中的查询。注意:当查询查询被激活时,QueryTimeout属性就已经传给ODBC驱动程序,所以若之后才改变这项属性将对已经被产生的查询没有作用。 使用WillExecute事件处理 这个事件会在执行任何查询前先发生,而不管是action或row-returning查询。可以藉由捕捉这个事件做执行查询前的最后修正工作项查询而RDO会产生可捕捉的错误。这个事件可以用来过滤某些查询是否可以执行。程序范例如下Option ExplicitDim WithEvents cn As rdoConnectionDim rs As rdoResultsetDim SQL As StringDim col As rdoColumnPublic Qd As rdoQueryDim dTime As Date Private Sub cn_Connect(ByVal ErrorOccurred As Boolean) If Not ErrorOccurred Then MsgBox "连结建立完成" Else Dim er As rdoError Debug.Print Err, Error$ For Each er In rdoErrors Debug.Print er.Description, er.Number Next End IfEnd Sub Private Sub cn_QueryComplete(ByVal Query As RDO.rdoQuery, ByVal ErrorOccurred As Boolean) If Not ErrorOccurred Then MsgBox Str(Second(Now - dTime)) Debug.Print Qd(0) Else Dim er As rdoError Debug.Print Err, Error$ For Each er In rdoErrors Debug.Print er.Description, er.Number Next End IfEnd Sub Private Sub cn_QueryTimeout( _ ByVal Query As RDO.rdoQuery, Cancel As Boolean) Dim ans As Integer ans = MsgBox("Query Timed out... Press Retry to continue waiting", _ vbRetryCancel + vbCritical, "Query Took Too Long") If ans = vbRetry Then Cancel = False Else Cancel = True End IfEnd Sub Private Sub RunQuery_Click() On Error GoTo RunQueryEH Qd.QueryTimeout = 5 Set rs = Qd.OpenResultset(rdOpenKeyset, _ rdConcurReadOnly) Exit Sub RunQueryEH: Dim er As rdoError Debug.Print Err, Error$ For Each er In rdoErrors Debug.Print er.Description, er.Number Next rdoErrors.Clear Resume Next End Sub Private Sub cn_WillExecute(ByVal Query As RDO.rdoQuery, Cancel As Boolean) dTime = NowEnd Sub Private Sub Form_Load() Set cn = New rdoConnection With cn .Connect = "uid=sa;pwd=;database=Pubs;dsn=Public;" .CursorDriver = rdUseClientBatch .EstablishConnection Prompt:=rdDriverNoPrompt, ReadOnly:=True, Options:=rdAsyncEnable .QueryTimeout = 5 End With Set Qd = cn.CreateQuery("LongQuery", "") With Qd .SQL = "{?=call pubs.dbo.VeryLongProcedure}" .rdoParameters(0).Direction = rdParamReturnValue End WithEnd Sub 异步地开启连结 在执行rdoConnection的EstablishConnection方法时,可以设定Options参数为rdAsyncEnable以异步地开启连结。若开启连结的动作很耗时;如在广域网络上开启连结,则这个方法将可以让使用者不至于因为程序执行权未还回导致停下来空等,而可以先做其它的事情,待连结完成后再处理需要连结数据库的事情。使用异步地开启连结需要注意以下的事情l l 设定LoginTimeout的时间够长(rdoEngine、rdoEnvironment、rdoConnection对象都有提供此属性,其中rdoEngine的属性为rdoDefaultLoginTimeout),以等待登入完成。l l 使用rdAsyncEnable的选项设定让执行权会还响应用程序。l l 以处理Connect事件代替polling StillConnection属性l l 使用BeforeConnect事件来警告使用者,建立连结的动作或许会很花时间。使用异步地开启连结最重要的就是让使用者得知现在系统的状况,而不会让应用程序在建立连结时,停在那儿好象当机一样,让使用者搞不清楚,甚至可能重新激活计算机。 使用独立的(stand-alone)rdoConnection对象 产生rdoConnection对象不一定要经由rdoEnvironment对象,而可以以下的程序代码产生独立的rdoConnection对象Dim varConnection As New rdoConnection或是Dim varConnection As rdoConnection…Set varConnection = New rdoConnection一但建立了独立的rdoConnection对象后,可以再行设定各种属性来指定伺服主机、光标型态、以及其它的设定。程序范例如下Dim MyCn As New rdoConnectionWith MyCn .Connect=”DSN=Public;DATABASE=Pubs;UID=sa;PWD=;” .CursorDriver=rdUseNone .LoginTimeout=5 .EstablishConnection(rdDriverNoPrompt)End With若想要将新产生的独立rdoConnection对象关联(associate)到特定的rdoEnvironment对象内,可以使用与该rdoEnvironment有关联的rdoConnections集合的Add方法加入,程序范例如下rdoEnvironments(0).rdoConnections.Add MyCn同样的可以rdoConnections的Remove方法移除某个rdoConnection对象。 使用rdoConnection对象 一但rdoConnection对象被建立,则可以完成下列事项l l 使用与某个rdoEnvironment有关的rdoConnections集合的Add方法,将rdoConneciton加入某个rdoEnvironment以使用交易的管理。l l 使用rdoConnectino对象的OpenResultset或CreateQuery方法产生rdoResultset或rdoQuery对象。l l 执行动作查询(action query)以称新数据库资料,更改数据库结构(schema),或执行管理的动作(administrative function)l l 执行预存程序来管理结果集、输出的参数或return valuesl l 使用rdoConnection的Close方法以停止到资料源的连结,同时释放所使用的资源。 与RDO资料源连结失败 可能因为以下的原因会造成与RDO资料源连结失败l l 没有使用资料源或网络的权限l l 网络连结错误或权限不对l l 没有资料源,或资料源没有致能(disabled)l l 没有正确安装或注册驱动程序,ODBC和网络协议的驱动程序都要安装与设定好l l 前端应用程序(可能一个或多个)已经建立了太多的连结l l 网络或远程资料服务器太忙碌,以致在LoginTimeout属性所设的时间已经到了还未来得及响应。应用程序应该要处理这些可能发生的问题,而VB所提供的错误处理可能响应的讯息过于简单,这时可以使用rdoErrors集合内存的错误讯息。 使用rdoTable对象 利用rdoTable对象可以对应到资料源的资料表(tables)或某些字段,抑或是产生rdoResultset对象。无论如何在rdoTables集合未被参照前,没有资料表的资料被传回。而以下是利用ListBox来列出连结的数据库内所有的资料表名称,当使用者以鼠标按下某一个名称后会引发Click事件,而在List2这个ListBox内会列出该资料表所有字段的名称。 Dim tb As rdoTable For Each tb In cn.rdoTables List1.AddItem tb.Name Next tb Private Sub List1_Click() Dim clm As rdoColumn List2.Clear For Each clm In cn.rdoTables(List1.ListIndex).rdoColumns List2.AddItem clm.Name Next clmEnd Sub 关掉不需要的RDO连结 设计多人使用的程序时,最好养成以Close方法;关掉当时不需要的RDO连结,以保持同时上线使用人的数量。当Connection对象被关闭后,任何该对象所拥有的对象都会被释放掉(released)。例如伺服端的光标或伺服端在TempDB所产生的任何相关对象。若这些对象要一值被维护,则Connection对象就不可以被关掉。RDO并不帮忙管理Connection对象,也就是当你关掉一个Connection对象时,它不会为你快取(cache)最近被关掉的连结,在需要时再直接赋予。而是直接关掉,当应用程序要求建立连结时,再重新建立。 § 使用RDO来送出查询 通常应用程序需要利用查询来完成对远程数据库资料、使用者权限或数据库伺服程序运作的管理。而查询可分为以下两部分l l 送出一个内含SQL述句描述所需结果集的查询,或参照一个预存程序l l 处理结果记录,参数和传回值(预存程序回传的部分)在送出一个查询前,需要问以下的问题以确认查询的目的与效率l l 应用程序是否需要浏览资料记录?若需要浏览,则是双向的还是祇向前浏览?l l 是否需要更新资料?如果需要,是否可以运作查询直接更改?使用者是否需要权限来修改?l l 有多少使用者会在同时,运作同样的资料?可否控制这些同时执行的应用程序?这些应用程序是否共享资料?l l 查询将会传回多少笔资料?应用程序端的系统是否有能力存放这些记录?网络使否在传这些记录时有频宽的瓶颈?l l 记录是以何种格式传回—书签还是静态资料?l l 查询是否要传参数?l l 这个查询在应用程序运作时会常常被执行,还是仅有一两次? 管理RDO查询 当开启一条连结时,可以在其上执行许多查询,可以参考下述原则使用不同形式的查询l l 若查询仅仅使用一次,可以用rdoConnection对象的OpenResultset或Execute方法来产生rdoResultset对象,还是直接执行动作查询。这两种选项最后都会经由执行ODBC API的SQLExecDirect函式完成查询,所以可以在使用OpenResultset或Execute方法时,直接在Options参数输入rdExecDirect以直接指定SQLExecDirect函式。l l 若执行的查询可能会使用一次以上同时要输入参数,则可以CreateQuery方法产生rdoQuery对象。该对象允许多次使用,且在使用时输入不同的参数。一但使用rdoQuery对象,则可以透过这个对象的OpenResultset或Execute方法来产生rdoResultset对象,或是直接执行动作查询。若要改变输入的参数,则可以经由改变rdoParameter对象办到。这种做法会经由ODBC API的SQLPrepare和SQLExecute函式。 产生rdoResultset对象 l l 使用rdoConnection对象的OpenResultset方法。这是透过传递一个查询以产生结果集,在程序运作中这个动作可能不会再重复,所以采用此方法。传回的结果集放在rdoResultset对象内。范例如下Dim rs As rdoResultsetDim cn As New rdoConnectionDim cl As rdoColumnDim SQL As StringConst None As String = "" cn.Connect = "uid=sa;pwd=;server=UI01;" _ & "driver={SQL Server};database=pubs;" _ & "DSN=’= rdUseOdbccn.EstablishConnection rdDriverNoPrompt SQL = "Select Au_Lname, Au_Fname" _ & " From Authors A" _ & " Where Au_ID in " _ & " (Select Au_ID" _ & " from TitleAuthor TA, Titles T" _ & " Where TA.Au_ID = A.Au_ID" _ & " And TA.Title_ID = T.Title_ID " _ & " And T.Title Like '" _ & InputBox("Enter search string", , "C") & "%')" _ & "Select * From Titles Where price > 10" Set rs = cn.OpenResultset(SQL, rdOpenKeyset, _ rdConcurReadOnly, rdAsyncEnable + rdExecDirect) Debug.Print "Executing ";While rs.StillExecuting Debug.Print "."; DoEventsWend Do Debug.Print String(50, "-") _ & "Processing Result Set " & String(50, "-") For Each cl In rs.rdoColumns Debug.Print cl.Name, Next Debug.Print Do Until rs.EOF For Each cl In rs.rdoColumns Debug.Print cl.Value, Next rs.MoveNext Debug.Print Loop Debug.Print "Row count="; rs.RowCount Loop Until rs.MoreResults = False l l 使用rdoQuery对象的OpenResultset方法。与前一方法同,可是这个查询可以重复使用。传回的结果集放在rdoResultset对象内,而传递的参数放在rdoQuery之下的rdoParameters集合内。 <, ;, DIV>Dim rs As rdoResultset Dim, , , , cn As New rdoConnectionDim qd As New rdoQueryDim cl As rdoColumnConst None As String = "" cn.Connect = "uid=sa;pwd=;server=UI01;" _ & "driver={SQL Server};database=pubs;" _ & "DSN='';"cn.CursorDriver = rdUseOdbccn.EstablishConnection rdDriverNoPrompt Set qd.ActiveConnection = cnqd.SQL = "Select * From Titles" _ & " Where CharIndex( ?, Title)【1】【3】 > 0" qd(0).Type = rdTypeCHARqd(0) = InputBox("Enter search string", , "C") Set rs = qd.OpenResultset(rdOpenForwardOnly, rdConcurReadOnly) For Each cl In rs.rdoColumns Debug.Print cl.Name,NextDebug.Print Do Until rs.EOF For Each cl In rs.rdoColumns Debug.Print cl.Value, Next rs.MoveNextDebug.PrintLoop 使用OpenResultset方法 OpenResultset的形式有两种,如下Set variable = connection.OpenResultset(name 【,type 【,locktype 【,option】】】)Set variable = object.OpenResultset(【type 【,locktype 【, option】】】)l l 第一种因为是利用rdoConnection对象直接开启rdoResultset对象,所以有name参数以提供SQL查询,第二种方法的Object可以是rdoQuery或rdoTable对象;若是用rdoQuery对象开启rdoResultset对象,则SQL查询设定在rdoQuery对象的SQL属性内,所以不需要name参数。若是rdoTable使用,则因为rdoTable内定的对象是针对整个资料表或是View,所以也不需要name参数。l l type参数是定义光标的型态,若要强制不要使用光标,可以使用rdUseNone。若不指定,则内定的型态是rdOpenForwardOnly。type参数可以指定的型态有keyset、dynamic、static、或forward-only。注意:不是所有的光标函式库都完整的支持以上的型态,若不支持会自动以另一种型态代替。l l locktype参数定义同时使用时的锁定型态。内定值是rdConcurReadOnlyl l option参数定义查询是否需要异步执行,以及使用哪个ODBC函式。内定值是同步执行,且使用的是SQLPrepare和SQLExecute API函式。 编写SQL述句 使用SQL的语法应该要符合资料来源的服务器所懂得的语法。以下是一些使用的提要l l 可以一字段(column)的名子指定要传回的字段或以“*”传回所有的字段,若有重复的字段名子,在指定上就要使用资料表的名子。也可以传回使用聚合(aggregate)函数的结果或数值计算过的字段,但要替这些字段取别名(alias),以利操作上的参考。l l 在关系数据库时,需以Where子句定义资料表之间相关的字段。l l 若要重复使用以Where子句定义的查询,则可以利用参数查询。l l 可以利用Order By或Group By子句来结构化查询结果。 § 使用参数查询 参数查询仅能用rdoQuery对象,建立该对象后,再以其下的rdoParameters集合设定其传输的参数。但使用参数查询要小心由使用者输入的参数是否合SQL的语法,或是参数型态是否正确。 以rdoParameters集合管理参数 在SQL述句内,原先要使用参数的地方可以问号“?”代替。这就像一个箩卜一个坑一样,为每一个输入、输出或输出入都可的参数定义一个保留的位置。而该参数是输出入与否,则利用rdoParameter对象的Direction属性来设定。当所有属性和值设定好后,RDO和ODBC接口会自动地做其间的rdoParameters与SQL述句的管理与转换。 在参数查询时使用正确的SQL述句 使用的方法有l l 连接字符串:可以VB内定的连接字符串运算子“&”来产生SQL述句,这种写法所产生的字符串变量可以用在OpenResult方法的Name参数,或是rdoQuery对象的SQL属性内。其程序范例如下sSQL=”SELECT Name, Age FROM Animals” _ & “ WHERE Weight > “ & WeightWanted.Text _ & “ AND Type = ‘ “ & TypeWanted.Text & “ ’ ”l l 原始的SQL语法:可以直接以SQL的语法来传输参数,RDO定义的参数位置标示便是与SQL原始语法用的相同,亦即“?”,这时有以rdoParameter对象来输入参数。范例如下sSQL=”SELECT Au_LName FROM Authors WHERE Au_Fname = ? “或是sSQL=”EXECUTE MyStoredProc ‘Arg1’, 450, ‘ “ & Text1 & “ ‘ “或是sSQL=”EXECUTE MyStoredProc ?,?,?【2】【4】”l l ODBC呼叫语法:以ODBC呼叫预存程序来传回状态值或输出参数,其标示变量的方法亦相同。其格式如下sSQL=”{CALL ParameterTest(?,?,?) }”或是sSQL=”{?=CALL ParameterTest(?,?,?)}”或是sSQL=”{?=CALL ParameterTest(?,?,14,’Pig’)}” 使用ODBC Call语法的好处 因为ODBC Call使用Open Data Systems Remote Procedure Call(ODS RPC)来执行查询,其特色为参数直接传给伺服端的预存程序而不做任何的分析,以判断是否有SQL语法错误。所以执行速度较快;且因为与SQL语法无关,所以对于不同的后端服务器有较佳的兼容性。 在参数查询使用rdExecDirect选项 使用rdExecDirect会直接呼叫ODBC的SQLExecDirect函数,而不是SQLPrepare。SQLExecDirect不会产生暂存的查询,以做SQL语法的分析,而是直接将传来的查询传给后端,所以若有ODBC驱动程序不允许但后端服务器允许的SQL语法,就可以使用这一选项。但使用参数查询时,最好不要用这个选项,因为会跳过ODBC接口先行检查各输入的参数与后端实际相对位置所用的参数型态是否相同。注意:在某些状况下ODBC接口所产生的暂存查询无法被释放掉,一直到连结结束。这种情况在使用rdExecDirect选项时,便不会发生。 不同使用选项的结论 下表为三种dding=0 border=1> 特色原始SQLODBC呼叫连接字符串 传递完整的SQL述句,而不呼叫伺服端的预存程序YesNoYes 执行预存程序YesYesYes 可否使用“?”来传递参数YesNoYes 可否处理传回值NoYesYes 可否处理输出参数NoYesYes 可否包含多个SQL字符串YesNoYes 典型的参数查询程序代码 以下为简单的SELECT述句形成的参数查询1. 1. 设定一个以“?”代替SQL查询中的参数部分的字符串sSQL=”SELECT * FROM Authors WHERE Au_LName=?” 2. 2. 接着产生rdoQuery对象以管理该查询以及相关的参数Set qryAuthorsLName = rdoCnn.CreateQuery(“”,sSQL) 3. 3. 接着以使用者在窗体中如TextBox对象的所输入的值当参数查询的参数值qryAuthorsLName.rdoParameters(0)=Text1.Text 4. 4. 因为rdoParameters为rdoQuery对象的内定对象,所以上列程序代码可以省略标示rdoParameters集合的部分,而写成如下的式子qryAuthorsLName(0)=Text1.Text 5. 5. 接着产生rdoResultset对象以处理查询所得的记录Set rdoRst=qryAuthorsLName.OpenResultset() 6. 6. 若使用者更改了Text1.Text的内容,则可以设定新的值后再以rdoResultset对象的Requery方法重新查询一次,以更新rdoResultset对象的内容,范例如下qryAuthorsLName(0)=Text1.TextrdoRst.Requery 整个的程序范例如下 Option ExplicitDim rdoCnn As New rdoConnectionDim rdoQry As rdoQueryDim rdoRst As rdoResultsetDim rdoClm As rdoColumn Private Sub Command1_Click() rdoQry(0).Value = Text1.Text rdoRst.Requery Do Until rdoRst.EOF For Each rdoClm In rdoRst.rdoColumns Me.Print rdoClm.Value, Next rdoRst.MoveNext Me.Print Loop Exit Sub errHandle: Dim oErr As rdoError For Each oErr In rdoErrors Me.Print oErr.Description Next oErrEnd Sub Private Sub Form_Load() On Error GoTo errHandle rdoCnn.Connect = "uid=sa;pwd=;server=UI01;" _ & "driver={SQL Server};database=pubs;" _ & "DSN='';" rdoCnn.CursorDriver = rdUseOdbc rdoCnn.EstablishConnection rdDriverNoPrompt Set rdoQry = rdoCnn.CreateQuery("", "Select * From Titles" _ & " Where title like ? ") rdoQry(0).Type = rdTypeCHAR rdoQry(0) = Text1.Text Set rdoRst = rdoQry.OpenResultset(rdOpenForwardOnly, rdConcurReadOnly) For Each rdoClm In rdoRst.rdoColumns Me.Print rdoClm.Name, Next Me.Print Exit Sub errHandle: Dim oErr As rdoError For Each oErr In rdoErrors Me.Print oErr.Description Next oErrEnd Sub当RDO执行Requery方法时,会先更新该查询的rdoParameters集合内的值,清掉rdoResultset对象,再将查询传到远程,重新产生新的rdoResultset对象。当查询对象第一次被产生时,RDO和ODBC层会在伺服端机器上产生一个暂存的查询,以接收参数。每当前端要执行一次该查询时,仅仅是赋予新的参数后,便再执行一次该暂存查询。 使用RDO执行预存程序 预存程序可以传回一个以上的结果集,或是再呼叫其它的预存程序,这在写作程序时需要注意。 典型的RDO使用预存程序的程序代码 预存程序的参数属性可以是仅输入、仅输出或是输出入皆可。在大部分情况下ODBC驱动程序都可以经由询问伺服端该预存程序的属性来判断输入参数的属性,而不需要程序设计者透过Direction属性来定义。下列步骤以使用MS SQL Server提供的SP_PASSWORD为例,解释典型RDO使用预存程序的程序代码写作1. 1. SP_PASSWORD可以输入三个参数:旧密码,新密码,使用者登入的名称。传回一个值代表更改成功或失败。但因为一般使用者祇能更改自己的密码,除非使用者是系统管理员。所以一般祇需要输入前两个参数。所以要产生一个重复使用的参数查询来呼叫预存程序代码的写法如下Dim qryChgPwd As rdoQuery,sQry As StringsQry=”{?=CALL SP_PASSWORD(?,?)}” 2. 2. 下一步为产生名为“SetPassword”的查询,SQLString参数即为上式的sQry,本行程序只需执行一次便会产生新的查询,并加入到rdoConnection对象的rdoQueries集合内。Set qryChgPwd=rdoCnn.CreateQuery(“SetPassword”,sQry) 3. 3. 下一步是设定参数的使用方向属性,亦即该各个参数是传入、传出亦或是传入传出都可以。内定值是rdParamInput。大部分的状况下是不需要设定rdoParameter对象的Direction属性,因为ODBC驱动程序会自动参阅在伺服端预存程序的参数属性。 这些参数的对应顺序即是“?”出现的顺序,在本例中第一个参数,也就是rdoParameters(0)对应的参数便是“?=”的问号位置;也就是预存程序sp_password的传回值。以此类推,第二个参数就是rdoParameters(1)对应的就是sp_password要输入的旧密码。所以,以下的程序代码可有可无qryChgPwd.rdoParameters(0).Direction=rdParamReturnValue 4. 4. 下一步是设定两个输入的参数,亦即旧新的密码。qryChgPwd(1)=””qryChgPwd(2)=”test” 5. 5. 接着再使用rdoQuery的Execute方法以执行该查询qryChgPwd.Execute 6. 6. 由传回的值可以判断成功或失败,由于sp_password在更改密码失败时,会传回非0值,所以藉以判断更改的成功或失败If qryChgPwd(0)<>0 ThenMsgBox “更改密码失败”End If 接收预存程序所输出的参数 1. 1. 使用ODBC语法以建立可使用输出入参数的参数查询。2. 2. 若ODBC资料源无法自动决定参数的方向,需要以rdoParameter对象的Direction属性来设定。3. 3. 若ODBC资料源无法自动决定参数的资料型别,需要以rdoParameter对象的Type属性来设定。4. 4. 以Execute方法执行该查询。5. 5. 当查询执行完,参照rdoParameter对象的内容以决定传回值或预存程序所输出的参数 传出参数的预存程序程序代码写作 下为在MS SQL Server上简单的预存程序,动作为仅仅将输入的参数设定到输出的参数,并传回2000IF EXISTS (SELECT * FROM sysobjects WHERE id = object_id('dbo.TestOutputPars') AND sysstat & 0xf = 4) DROP PROCEDURE dbo.TestOutputParsGO CREATE PROCEDURE TestOutputPars @InputPar1 INTEGER=10, @InputPar2 VARCHAR(15)='Test',@OutputPar1 INTEGER OUTPUT,@OutputPar2 VARCHAR(15) OUTPUT ASSELECT @OutputPar1=@InputPar1SELECT @OutputPar2=@InputPar2RETURN 2000GO 下例为传出参数的预存程序程序代码 On Error GoTo errHandle Dim sSQL As String, MyOutputVal1 As Variant Dim MyOutputVal2 As Variant, myRetVal As Variant Dim rdoCnn As New rdoConnection With rdoCnn .Connect = "DSN=Public;uid=sa;pwd=;DATABASE=Pubs" .EstablishConnection rdDriverNoPrompt End With sSQL = "{?=CALL pubs.dbo.TestOutputPars(?,?,?,?)}" Dim rdoQry As rdoQuery Set rdoQry = rdoCnn.CreateQuery("TestOutput", sSQL) rdoQry(1) = 1 rdoQry(2) = "Hello World" rdoQry(3).Direction = rdParamOutput【3】【5】 rdoQry(4).Direction = rdParamOutput rdoQry.Execute Me.Print rdoQry(0); rdoQry(3); rdoQry(4) exitSub: On Error Resume Next rdoQry.Close Set rdoQry = Nothing rdoCnn.Close Set rdoCnn = Nothing Exit Sub errHandle: Dim oErr As rdoError For Each oErr In rdoErrors Me.Print oErr.Description Next oErr Resume exitSub § 产生RDO光标 应为使用光标使很耗整体系统资源的,所以在客户端/伺服端模式下最好不要使用光标,除非在以下的状况下可以考虑使用l l 在限量的结果集中来回浏览l l 利用书签直接移动到某一特定的记录l l 以绝对或相对的值在结果集中直接移动到某笔记录。l l 以RemoteData控件对基础资料表做更新限量的结果集在以Select这个SQL述句产生结果集时,绝对不要不限制的使用(亦即不搭配WHERE子句)。否则不但造成系统负荷过重、对底层资料表的安全问题,且也影响多人同时使用,造成锁定的问题。 选择RDO光标链接库 RDO支持好几个光标链接库,每一个光标链接库有不同的特性。并不是所有的光标链接库都支持所有型态的光标;仅仅基本上都支持forward-only型态的光标。例如;ODBC Client-side仅支持rdOpenStatic和rdOpenForwardOnly型态的光标,而SQL Server server-side则支持所有的型态。选择适当的光标链接库是必须的,例如若要产生分离的rdoResultset对象以使用BatchUpdate方法,则要使用client batch光标链接库。在建立独立的rdoQuery对象时,要指定光标链接库必须在使用Execute方法之前,在建立之后,则没有办法再改变。在使用OpenResultset方法时,可以藉由设定type参数来设定CursorType。要设定光标链接库则可利用rdoEngine的rdoDefaultCursorDriver属性,或是利用rdoEnvironment或rdoConnection对象的CursorDriver属性。下表描述这些属性可用的值 选项说明 rdUseODBCRDO将使用ODBC光标链接库。这对小的结果集有较好的执行效率,反之对大的结果集效果不好。 rdUseServer若伺服端支持的话,RDO将使用伺服端的光标。 rdUseClientBatchRDO将使用客户端批次光标链接库。 rdUseIfNeeded内定值。ODBC驱动程序将自动选择较适合的光标链接库,如果伺服端的光标可以用的话,会尽量使用伺服端光标。 rdUseNoneRDO建立没有光标的结果集。 使用结果集更改资料 Dim er As rdoErrorDim cn As New rdoConnectionDim qy As New rdoQueryDim rs As rdoResultsetDim col As rdoColumn Private Sub command1_Click() On Error GoTo ANEH With rs .AddNew !job_desc = Text1.Text !min_lvl = Text2.Text !max_lvl = Text3.Text .Update End With Exit Sub UpdateFailed: MsgBox "Update did not suceed." rs.CancelUpdate Exit SubANEH: Debug.Print Err, Error For Each er In rdoErrors Debug.Print er Next Resume UpdateFailed End Sub Private Sub Form_Load() cn.CursorDriver = rdUseOdbc cn.Connect = "uid=sa;pwd=;server=UI01;" _ & "driver={SQL Server};database=pubs;dsn='';" cn.EstablishConnection With qy .Name = "JobsQuery" .SQL = "Select * from Jobs" .RowsetSize = 1 Set .ActiveConnection = cn Set rs = .OpenResultset(rdOpenKeyset, _ rdConcurRowVer) Debug.Print rs.Updatable End With End Sub 多结果集的程序范例 On Error GoTo errHandle Dim sSQL As String, MyOutputVal1 As Variant Dim MyOutputVal2 As Variant, myRetVal As Variant Dim rdoCnn As New rdoConnection With rdoCnn .Connect = "DSN=Public;uid=sa;pwd=;DATABASE=Pubs" .CursorDriver = rdUseNone .EstablishConnection rdDriverNoPrompt End With sSQL = "SELECT * FROM Titles WHERE Title LIKE 'c%';SELECT * FROM Titles WHERE Title LIKE 'f%'" Dim rdoQry As rdoQuery Set rdoQry = rdoCnn.CreateQuery("TestOutput", sSQL) Dim rdoRst As rdoResultset Set rdoRst = rdoQry.OpenResultset Dim rdoClm As rdoColumn Do While Not rdoRst.EOF For Each rdoClm In rdoRst.rdoColumns Debug.Print rdoClm.Value; Next rdoClm rdoRst.MoveNext Debug.Print Wend Debug.Print "------------------Next Resultset-------------------" Loop While rdoRst.MoreResults exitSub: On Error Resume Next rdoQry.Close Set rdoQry = Nothing rdoCnn.Close Set rdoCnn = Nothing Exit Sub errHandle: Dim oErr As rdoError For Each oErr In rdoErrors Me.Print oErr.Description Next oErr Resume exitSub 【1】【3】 CHARINDEX('pattern', expression):用来查询在expression字符串内第几个位置开始pattern,会将位置的数值传回。上述范例中用来查询哪几笔资料的title字段有使用者输入的字符串。【2】【4】 这种直接的语法,也就是以MS SQL Server所提供的ISQL_w、ISQL或是SQL Enterprice Manager内可以直接下来执行的SQL述句。【3】【5】 经测试MS SQL Server的ODBC驱动程序似乎可以判断输出入参数的资料型别(回传值只有Integer型别,所以不需资料型别的判断);回传值、输入参数的方向,但输出参数的方向一定要设定。擷取自Visual Basic 5.0線上手冊 Guide to Building Client/Server Application with Visual Basic Part3: Data Access Options Using Data Access Objects with Remote Database【1】[1] |
随便看 |
百科全书收录594082条中文百科知识,基本涵盖了大多数领域的百科知识,是一部内容开放、自由的电子版百科全书。