ODBCDirectワークスペースの利用

著者:新居雅行/Masayuki Nii


このWebページでは、『Visual Basic 5.0 データベース構築法』で解説をしなかった、DAO Ver.3.5の新機能であるODBCDirectについての利用法を説明しています。基本的には前記の書籍の読者向け追加情報です。そのため、Visual BasicやDAOによるプログラミングの知識があることを前提にしています。
●ODBCDirectワークスペースの位置付け

DAOを利用することで、Accessのデータベースファイルを中心に、ODBCのデータソースもリンクなどで利用することができます。いずれにしても、Accessのデータベースファイルを中心とした処理になっています。この状態での利用は「Jetワークスペース」と呼ばれています。データベースエンジンとして、Jetデータベースエンジンを利用している状態です。これがDAOの既定の状態と言えるでしょう。

これに対して、DAO自身がJetデータベースエンジンを使わずに、ODBCを直接利用するような動作形態も用意されています。これを「ODBCDirectワークスペース」と呼んでいます。DAOという枠組みの中で、Accessのファイルを利用せずに、直接ODBCを利用するような形態です。これは、RDO(Remote Data Object)の利用とも違う形態です。

ODBCDirectにより、ODBCを直接利用できます。たとえば、SQL Serverに接続することができます。そのため、Accessのファイルを利用する時との若干の相違があるのですが、大きな特徴は、いったんRecordsetを確保してしまうと、後の処理は通常のAccessのファイルを利用したDAOデータベースの処理とあまり変わらないというところにあります。いわば、DAOのやり方で、SQL ServerなどODBCデータソースを直接操作できるというところでしょう。言い換えれば、DAOで作ったプログラムをあまり修正せずに、ODBCのデータベースに直接やりとりできるようになるということにもつながります。

●ワークスペースの確保

Accessのデータベースファイルを利用する時には、既定のワークスペースをそのまま使うのが一般的でした。ユーザー名を指定するようなときには、ワークスペースを作成しました。これに対して、ODBCDirectでは、まず、ODBCDirectワークスペースを作成しなければなりません。作成など、基本的な操作は、以下のようなメソッドを利用します。


[DBEngine.]CreateWorkspace(Name, User, Password [,Type])
新しいワークスペースを作成し、そのWorkspaceオブジェクトへの参照を戻す。Typeに定義定数のdbUseODBCを指定すると、ODBCDirectワークスペースを作成する。Nameにオブジェクト名、User、Passwordに接続に利用するユーザー名とパスワードを指定する。

<Workspace>.Close
ワークスペースを破棄する

ユーザー名の指定などがありますが、ここで必ず指定しなければならないわけではありません。ワークスペースなので実際の接続処理までは行いません。ここでは、引数TypeにdbUserODBCを指定したワークスペースを新たに作成して、その参照を変数に保存しておくというのがポイントになります。

●カーソルとカーソルドライバ

Accessのデータベースでは、Recordsetとして、テーブルあるいはクエリー結果を利用できました。そこでは、「カレントレコード」として、テーブル形式の特定のレコード(行)に注目して処理をするということを行うことを説明しました。

SQL Serverなど、データベースサーバーでも、同様の機能を持っており、一般に「カーソル」と呼ばれています。データベースサーバーはSQLという問い合わせ言語によって記述された処理を行うというのがごく基本的な動作です。しかしながら、SELECTやUPDATEのような基本的なSQLコマンドでは、表形式のデータの処理は記述できますが、その表の各行に対しての処理ということはそのままでは記述できなかったのです。そこで、SQLコマンドを拡張し、「カーソル」という機能を導入します。たとえば、SELECTコマンドによって、データベースサーバーから一連の表形式のデータが取り出されます。その結果に対して、カーソルを定義するということを行います。そして、カーソルによって「現在のレコード」が管理され、現在のレコードに対する更新処理もSQLで記述できるようになっています。

こうした機能がサーバー側で管理されるのが一般的です。つまり、クライアントのプログラムでは、サーバーのカーソル機能を利用して、「現在のレコード」を管理するということをします。そうしないと、DAOのような形態の処理では、いったんデータを全部クライアントにダウンロードしないといけないというような事態にもなりかねません。しかしながら、サーバー側でカーソルが機能していれば、必要最低限のデータのやりとりで済むことになり、処理の効率化が期待できます。

ODBCDirectワークスペースでも、サーバーのカーソル機能を利用することができます。ただし、基本的にはサーバーの機能にも依存します。そのための設定機能などがありますが、利用するデータベースサーバーの対応状況もよく加味する必要はあるでしょう。

ただ、ODBCは自分自身にもカーソル機能を持っています。ODBCで利用するデータベースが常にカーソルをサポートしているとは限りません。そのために、内部でもカーソル機能をサポートし、「カレントレコード」という処理対象をクライアントで利用できるようにしています。そのときは、クライアント側でカーソルを実現しているということになります。ネットワークの先にあるデータベースサーバーに対し、比較的データ量が多い場合には、サーバーでカーソル管理する方が効率は高くなるでしょう。しかしながら、少ないデータの場合にはネットワークの先にあっても、クライアント側でカーソル管理した方が、かえって効率良くなることもあるとされています。

ODBCDirectワークスペースに対して、以下のようなプロパティが利用でき、これによって利用するカーソル機能を指定できます。実際の接続の前に、このプロパティに設定をしておかなければなりません。


<Workspace>.DefaultCursorDriver
カーソルドライバの種類を示す定義定数

▼DefaultCursorDriverプロパティで利用できる定義定数
定義定数数値ドライバ
dbUseDefaultCursor-1データベースでカーソルが利用できるならそれを利用。できないなら、ODBCのカーソル機能を利用する(既定値)
dbUseODBCCursor1ODBCのカーソル機能を利用する
dbUserServerCursor2サーバーのカーソル機能を利用する
dbUserClientBatchCursor3クライアントでカーソルを管理する「バッチ更新」処理を行う
dbUserNoCursor4Recordsetを作った時、前方スクロールで読み取り専用とする

「バッチ更新」は、データの更新があったときに、ロックせず、さらに実際の更新処理も行わずにクライアント側に更新処理をためておくような動作をします。Recordsetを作成するときに、引数LockTypeにdbOptimisticBatchを指定する必要があります。更新はUpdateメソッドを使います。

●データベースへの接続

ワークスペースを確保して、次に行うことは、実際にデータベースに接続することです。その接続のためのオブジェクトとして、DAOにはConnectionオブジェクトが定義されています。このオブジェクトは、Databaseオブジェクトに対応するものと考えれば良いでしょう。

接続のためには、ODBCコントロールパネルで、データソースをまず定義しておく必要があります。そのODBCデータソースを指定して、Connectionオブジェクトを確保します。そのとき、Connectionオブジェクトとともに、対応したDatabaseオブジェクトが作成されます。ODBCDirectでのDatabaseオブジェクトは、JetワークスペースのときのようなAccessのデータベースファイルに対応するものとは違い、DAOの処理を機能させるためのものだと考えれば良いでしょう。実態はODBCを直接利用しているのですが、そうしてデータベースに接続した結果をDatabaseオブジェクトとして見せることで、根幹部分の処理をJetでのDAOの処理と共通化させようというわけです。

以下のようなメソッドを利用します。OpenConnectionでも、OpenDatabaseでもどちらでも、データソースへの接続が行われ、ConnectionとDatabaseオブジェクトが利用できるようになります。


<Workspace>.OpenConnection(Name [,Options][,ReadOnly][,Connect])
ODBCを利用したConnectionオブジェクトを作成し、それへの参照を戻す。引数Optionsについては別表を参照、ReadOnlyはTrueなら読み取りのみで開く。他の引数の設定については本文を参照

<Workspace>.OpenDatabase(DbName [,Options][,ReadOnly][,Connect])
引数DbNameにデータソース名を指定すれば、ODBCを利用したConnectionオブジェクトを作成し、それに対応したDatabaseオブジェクトへの参照を戻す。引数については、OpenConncectionと同様

<Database>.Connection
対応するConnectionオブジェクトへの参照を戻す (R/O)

<Conncection>.Database
対応するDatabaseオブジェクトへの参照を戻す (R/O)

▼OpenConnectionメソッドのOptionsで利用する定義定数
定義定数設定
dbDriverNoPrompt1引数NameとConnectに指定された情報をもとに、接続を試みる
dbDriverPrompt2NameとConnectに指定された情報をもとに、それらに対応した「ODBCデータソース」ダイアログボックスを表示して接続を行う
dbDriverComplete0Connectに十分な情報があればそれを元に接続する。ない場合には、ダイアログボックスを表示する(既定値)
dbDriverCompleteRequired3Connectに十分な情報があればそれを元に接続する
dbRunAsync1024他の定数と加算して使い、非同期で接続を実行する

▼OpenConnectionメソッドの引数と動作
NameConnect動作
任意の文字列"ODBC;DSN=データソース名"Connectで指定したデータソースで接続する
接続で利用するデータソース名""Nameで指定したデータソースで接続する
データソースではない""ODBCデータソースを選択するダイアログボックスを表示する(Optionsでの設定も考慮する)

結果的に、Workspaceオブジェクトに、複数のConnectionやDatabaseオブジェクトがコレクションとして含まれることになります。コレクショントしての参照も可能ですが、通常はこれらのオブジェクトをプログラム中で作成するので、そのときの戻り値を変数に代入しておくのが一般的です。

●データベースに接続する

SQL Serverに接続することを行ってみますが、SQL Serverには、IDが「guest」、パスワードが「guest」のログインを定義しておき、このユーザーがpubsデータベースを利用できるようにしてあるとします。

まず、登録したデータソースに一気に接続してしまうには、次のようにワークスペースにユーザー名を指定するのが良いでしょう。

Dim ws As Workspace, con As Connection
Set ws = CreateWorkspace("ODBC-D", "guest", "guest", dbUseODBC)
Set con = ws.OpenConncection("NTSQL", dbDriverNoPrompt)

コントロールパネルで、「ユーザーDSN」として、「NTSQL」という定義を追加しておきます。そこでは実質的にはSQL Serverが機能しているNT Serverのコンピュータ名だけを指定しているだけです。これで、特にダイアログボックスが出るなどせずに、一気に接続が完了します。OpenConnectionの第一引数は指定せず、引数Connectに「"ODBC;DSN=NTSQL;UID=guest;PWD=guest"」のような文字列を指定しても、自動的に接続をします。

ODBC接続のための、ユーザー名とパスワードだけを入力するダイアログボックスを表示して、その都度ユーザーに入力させるには、たとえば次のように指定をすればよいでしょう。ユーザー名などをプログラムに含めずに、OpenConnectionの引数としてdbDriverCompleteを指定します。

Dim ws As Workspace, con As Connection
Set ws = CreateWorkspace("ODBC-D", "", "", dbUseODBC)
Set con = ws.OpenConncection("", dbDriverComplete, _
False, "ODBC;DSN=NTSQL")

さらにデータソースの指定もプログラム中からなくしてしまえば、どのデータソースを利用するかを選択するダイアログボックスが表示されます。そこでは、マシンデータソース(ユーザーDSNとシステムDSN)およびファイルDSNから選択できます。OpenConnectionなどのメソッドで指定可能なデータソース名はマシンデータソースだけですので、ファイルDSNからデータベース接続したい時には、ダイアログボックスで選択させるしかないでしょう。データソースのダイアログボックスを表示させるには、いちばん単純には、次のようなプログラムでかまいません。

Dim ws As Workspace, con As Connection
Set ws = CreateWorkspace("ODBC-D", "", "", dbUseODBC)
Set con = ws.OpenConncection("")

●Recordsetの確保

接続を行って、Connectionオブジェクトを確保すると、そこからRecordsetオブジェクトを確保して、データベース処理を行います。Recordsetを得れば、後は基本的には通常のDAOの処理と同様に、データの読み書きなどが行なえます。レコードセットの確保は、Jetワークスペースのときと同様、OpenRecordsetメソッドを使いますが、ODBCDirect特有の事情を踏まえて引数の指定が必要になります。


<Connection/Database>.OpenRecordset(Source [,Type][,Options][,LockEdits])
JetワークスペースのOpenRecordsetと同様。引数Sourceにはテーブル名やSQLステートメントなどの文字列を指定して、元データを指定する。その他の引数は表を参照

<Recordset>.NextRecordset
複数のSELECTを利用したクエリーで構成されたRecordsetに対して、2つ目以降のSELECTステートメントの実行結果を取り出す。クエリー結果を取りだせればTrueが戻される。クエリー結果は適用したRecordsetオブジェクトから参照する

<Recordset>.Close
レコードセットを閉じる

<Recordset>.Cancel
非同期のクエリーを止める

<Recordset>.Connection
Recordsetを所有するConnectionオブジェクト

<Recordset>.StillExecuting
非同期クエリーの処理が完了しているかどうかを示す論理値

▼ODBCDirectでのOpenRecordsetの引数指定
引数定義定数<動作/th>
TypedbOpenDynamic動的カーソルを利用する
dbOpenDynasetキーセットカーソルを利用する
dbOpenShapshot静的カーソルを利用する
dbOpenForwardOnly前方スクロールタイプになる
OptionsdbRunAsync非同期クエリーとして実行
dbExecDirectクエリーの直接実行
dbConsistentダイナセットやスナップショットで一貫性のある更新のみ
LockEditsdbReadOnly読み込みのみ(既定値)
dbPessimistic排他ロック。Editメソッドでロックがかかる
dbOptimistic共有ロック。Updateメソッドの時までロックされない
dbOptimisticValueレコードの更新を行うとき、取り込んだ値が変化ないかを調べることで、別のユーザーなどによる更新がなされていないかをチェックする
dbOptimisticBatchバッチ更新を行う

まず、カーソルの動作を選択する必要があります。動的カーソルだと、Recordsetを開いた後に、そこに含まれるレコードが別のユーザーなどによって更新されたとき、その更新結果を参照することができます。一方、静的カーソルでは、開いた後のRecordsetに関連するデータが変更されても、それは反映されません。静的カーソルは、読み取り専用になっており、Recordsetを確保したときにデータのセットを作ってしまうというわけです。前方スクロールタイプは、動的カーソルと同様だと考えてかまいませんが、カーソルは後ろのレコードに向かう方向にしか移動できません。つまり、前から順番にチェックすることしかできないと考えればよいでしょう。

キーセットカーソルは、Recordsetの各レコードを識別するためのキー情報を付加して管理します。別のユーザーなどによってその後に更新があれば、その更新結果は参照できますが、新しく追加された場合には認識できないということになります。別のユーザーなどに削除されたレコードについてもキーを持っており、それを取り込もうとするとエラーになります。

これらカーソルの違いはちょっと分かりにくいところですが、マスターの選択肢一覧構築などのような、とりあえず読み込みだけで良い場合には、静的カーソルを利用することになるでしょう。更新処理などを伴うときには、動的カーソルあるいはキーセットカーソルを使いますが、動的カーソルはデータベース側の変更を完全にフォローできるのに対して、キーセットではフォローできないこともあるということです。ただし、ブックマークは、動的カーソルではサポートされていません。ブックマークを利用するにはキーセットカーソルを利用します。一定のレコードに対して順繰りに更新をかけるような場合には、前方スクロールでも十分でしょう。

OpenRecordsetの最後の引数の既定値が、リードオンリになります。更新や追加が必要な場合には、忘れないように、この引数を指定しましょう。

OpenRecordsetの最初の引数に、SELECTステートメントを複数指定することができます。そのとき、テキストとして各SELECTステートメントを記述しますが、区切り記号としてセミコロン(;)を使います。このとき、OpenRecordsetで参照されるRecordsetは最初のSELECTの結果です。2つ目以降の結果を取り出すには、NextRecordsetを適用します。すると、OpenRecordsetで得られた参照先が、次の結果へと更新されます。

●ODBCDirectのRecordsetでの処理

こうしてRecordsetを確保すれば、基本的にはAccessのデータベースファイルのときと同様に処理ができます。プロパティについては、基本的にはすべて利用できますが、メソッドについては、以下のものが利用できるとして一覧されています。

▼Recordsetで利用できるメソッド
分類メソッド
カレントレコード移動Move、MoveFirst、MoveLast、MoveNext、MovePrevious
データの読み込みGetRows、GetChunk、AppendChunk
データの更新AddNew、Edit、Update、CancelUpdate

また、BeginTrans、ComitTrans、Rollbackといったトランザクション処理のメソッドも、データベースエンジンで利用できれば使えるようになっています。

●バッチ更新

更新可能なRecordsetを作成してレコードの更新をしたとき、バッチ更新という機能を利用すると、即座に更新が行われず、後からまとめて更新を行うことができます。DefaultCursorDriverプロパティにdbUserClientBatchCursorを指定して接続を行い、さらにRecordsetを作成するときに、引数LockTypeにdbOptimisticBatchを指定することで、バッチ更新可能なRecordsetが作成されます。

更新は、Editメソッドなどを利用することになりますが、実際の更新処理は、以下のようなメソッドで行います。


<Recordset>.Update([Type][, Force])
バッファの内容をRecordsetに対して更新する。通常はバッファからRecordsetへの転送を行うが、バッチ更新のときの更新処理でも利用する。引数Typeはバッチ更新時のみ指定でき、dbUpdateBatichならすべて書き込み、dbUpdateCurrentRecordならカレントレコードのみが更新。Forceを省略するかFalseを指定すると、別のユーザーによる更新によって矛盾のある更新になるときにはエラーが発生する。Trueにすると無条件に上書きする

<Recordset>.BatchCollisionCount
バッチ更新で更新できなかったレコード数。0なら正常に更新された

<Recordset>.BatchCollisions
バッチ更新で更新できなかったレコードのブックマークの配列

<Recordset>.RecordStatus
バッチ更新によって更新されたカレントレコードの状態を示す定義定数。あるいは、修正があったかを記録するため、次のバッチ更新によってどうなるかも示される(定義定数はヘルプを参照)

<Recordset>.BatchSize
バッチ更新において1度にサーバーに送付するコマンドの数を設定する

●一時的なクエリー定義の作成

ODBCDirectワークスペースでも、CreateQueryDefを利用してクエリーを作成することができます。使用方法については、基本的にはJetデータベースの場合と同様です。基本的には、RDOのrdoQueryオブジェクトと同様な扱いになると考えればよいでしょう。QueryDefオブジェクトに対してOpenRecordsetを適用したり、Executeを利用します。CreateQueryDefで作られたオブジェクトをコレクションから取り除くのは、Closeメソッドを利用します。

●ODBCDirectでできないこと

ODBCDirectワークスペースでは、DAOのオブジェクト階層が通常とはやや異なり、基本的には少なくなると考えて下さい。たとえば、TableDefやRelationオブジェクトは利用できませんし、QueryDefはその意味あいが違ってきています。オブジェクトの構成としては、むしろRDOに近いものになっています。

ODBCDirectの全体像を大雑把に表現すれば、RDOの処理をDAOのメソッドやプロパティで記述するようなものだと言えるかもしれません。


著者:新居雅行/Masayuki Nii
『Visual Basic 5.0 データベース構築法』読者サポートページ