Remote Data Objects

Remote Data Objects

RDO(Remote Data Objects,遠程數據對象) 是微軟的一個應用程式接口 (application program interface, API) ,讓寫Windows應用程式的程式師能夠進入微軟和其他的資料庫供給者的資料庫。然後,程式中 RDO 陳述使用微軟的底層數據存取對象 (DAO) 來實際進入資料庫。資料庫供給者寫入 DAO接口。 RDO 已經發展到微軟為新的程式推薦的程式接口--- ActiveX 數據對象。 ADO也提供進入非關係資料庫的接口而且比較容易使用。

中科永聯高級技術培訓中心(www.itisedu.com)

RDO (Remote Data Objects) 遠程數據對象是一個到 ODBC 的、面向對象的數據訪問接口,它同易於使用的 DAO style組合在一起,提供了一個接口,形式上展示出所有 ODBC 的底層功能和靈活性。儘管 RDO 在很好地訪問 Jet 或 ISAM 資料庫方面受到限制,而且它只能通過現存的 ODBC 驅動程式來訪問關係資料庫。但是,RDO 已被證明是許多 SQL ServerOracle 以及其他大型關係資料庫開發者經常選用的最佳接口。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】

Remote Data Objects

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
rdoConnection rdoEnvironment(x).OpenConnection(也可以用RemoteData控制項產生) Dim MyCn as New rdoConnection
rdoQuery rdoConnections(x).CreateQuery Dim MyQry as New rdoQuery
rdoResultset rdoQuery(x).OpenResultset rdoConnection(x).OpenResultset
rdoParameter 會為有參數的查詢自動產生
rdoTable 當被參照時自動產生
rdoError 當ODBC錯誤發生或有訊息時自動產生

RDO與Jet/DAO的比較

雖然RDO與DAO大致相似,但RDO是專門操作關係型資料庫的對象,且沒有自己的查詢處理(processor),它完全靠資料來源處理查詢並產生結果集 以下是RDO與DAO/Jet 的比較表
RDO對象 相對的DAO/Jet對象
rdoEngine DBEngine DBEngeine
rdoError Error Error
rdoEnvironment Workspace Workspace
rdoConnection Database Connection
rdoTable TableDef 不支持
不支持 索引 不支持
rdoResultset Recordset Recordset
不支持 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)
rdoColumn Field Field
rdoQuery QueryDef QueryDef
rdoParameter Parameter Parameter
不支持 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 rdoEnvironment Set 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屬性來建立連結對象rdoConnection l 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圖示。 可以在以下方法中用到DSN l 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屬性 內定值 設定
LoginTimeout 15 在放棄建立連結前要等多少秒
CursorDriver rdUseIfNeeded 使用該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 Explicit Dim WithEvents cn As rdoConnection Dim rs As rdoResultset Dim SQL As String Dim col As rdoColumn Public Qd As rdoQuery Dim 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 If End 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 If End 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 If End 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 = Now End 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 With End 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 rdoConnection With 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 values l 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 clm End 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 rdoResultset Dim cn As New rdoConnection Dim cl As rdoColumn Dim SQL As String Const None As String = "" cn.Connect = "uid=sa;pwd=;server=UI01;" _ & "driver={SQL Server};database=pubs;" _ & "DSN=’= rdUseOdbc cn.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 "."; Doevents wend 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 rdoConnection Dim qd As New rdoQuery Dim cl As rdoColumn Const None As String = "" cn.Connect = "uid=sa;pwd=;server=UI01;" _ & "driver={SQL Server};database=pubs;" _ & "DSN="";" cn.CursorDriver = rdUseOdbc cn.EstablishConnection rdDriverNoPrompt Set qd.ActiveConnection = cn qd.SQL = "Select * From Titles" _ & " Where CHARINDEX( ?, Title)【1】【3】 > 0" qd(0).Type = rdTypeCHAR qd(0) = InputBox("Enter search string", , "C") Set rs = qd.OpenResultset(rdOpenForwardOnly, rdConcurReadOnly) 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

使用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參數定義同時使用時的鎖定型態。內定值是rdConcurReadOnly l 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> 特色 原始SQL ODBC呼叫 連線字元串 傳遞完整的SQL述句,而不呼叫伺服端的預存程式 Yes No Yes 執行預存程式 Yes Yes Yes 可否使用“?”來傳遞參數 Yes No Yes 可否處理傳回值 No Yes Yes 可否處理輸出參數 No Yes Yes 可否包含多個SQL字元串 Yes No Yes

典型的參數查詢程式代碼

以下為簡單的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.Text rdoRst.Requery 整個的程式範例如下 Option Explicit Dim rdoCnn As New rdoConnection Dim rdoQry As rdoQuery Dim rdoRst As rdoResultset Dim 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 oErr End 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 oErr End 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 String sQry=”{?=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 Then MsgBox “更改密碼失敗” End If

接收預存程式所輸出的參數

1. 1. 使用ODBC語法以建立可使用輸出入參數的參數查詢。 2. 2. 若ODBC資料源無法自動決定參數的方向,需要以rdoParameter對象的Direction屬性來設定。 3. 3. 若ODBC資料源無法自動決定參數的資料型別,需要以rdoParameter對象的Type屬性來設定。 4. 4. 以Execute方法執行該查詢。 5. 5. 當查詢執行完,參照rdoParameter對象的內容以決定傳回值或預存程式所輸出的參數

傳出參數的預存程式程式代碼寫作

下為在MS SQL Server上簡單的預存程式,動作為僅僅將輸入的參數設定到輸出的參數,並傳回2000 IF EXISTS (SELECT * FROM sysobjects WHERE id = object_id('dbo.TestOutputPars') AND sysstat & 0xf = 4) DROP PROCEDURE dbo.TestOutputPars GO CREATE PROCEDURE TestOutputPars @InputPar1 INTEGER=10, @InputPar2 varchar(15)="Test", @OutputPar1 INTEGER OUTPUT,@OutputPar2 VARCHAR(15) OUTPUT AS SELECT @OutputPar1=@InputPar1 SELECT @OutputPar2=@InputPar2 RETURN 2000 GO 下例為傳出參數的預存程式程式代碼 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屬性。下表描述這些屬性可用的值
選項 說明
rdUseODBC RDO將使用ODBC游標程式庫。這對小的結果集有較好的執行效率,反之對大的結果集效果不好。
rdUseServer 若伺服端支持的話,RDO將使用伺服端的游標。
rdUseClientBatch RDO將使用客戶端批次游標程式庫。
rdUseIfNeeded 內定值。ODBC驅動程式將自動選擇較適合的游標程式庫,如果伺服端的游標可以用的話,會儘量使用伺服端游標。
rdUseNone RDO建立沒有游標的結果集。

使用結果集更改資料

Dim er As rdoError Dim cn As New rdoConnection Dim qy As New rdoQuery Dim rs As rdoResultset Dim 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 Sub ANEH: 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

相關詞條

相關搜尋

熱門詞條

聯絡我們