クライアント/サーバーにも本格的に対応した

VisualBasic 4.0 のデータアクセス機能

日本語版が発売されなかったVisualBasic 3.0の機能で、誰もが使いたかったのがデータアクセスとOLE 2.0の機能だった。中でもデータアクセス機能は、Accessコンパチなデータベースが扱える上に、ODBC経由で安価になってきたPC用クライアント/サーバー型RDBMSにもアクセスできる。そして、ついに発表されたVisualBasic 4.0では、クライアント/サーバー型RDBMSに合わせて、大幅な強化が行われた。ここでは、その概要について紹介しよう。

酒井 法雄

VisualBasicとデータアクセス

コンピュータにやらせたい仕事はたくさんあるが、コンピュータと切っても切り放せないのがデータベースだろう。ところが、意外なことにVisualBasic 2.0まではデータベースエンジンが搭載されていなかった。それ以前のDOS用BasicにはISAMライブラリなどがバンドルされていたというのにである。

しかし、Microsoft Accessが発売され、VisualBasicも3.0となってデータベースエンジンJetが搭載された。これは、Access 1.1とコンパチビリティーのあるものであった。JetはAccessの.MDB形式のファイルの読み書きができるだけでなく、ISAM系のドライバやODBCを使ったクライアント/サーバー型RDBMSへの接続などもできるようになっていた。

折しも、従来はハードウェア、ソフトウェアともに高価だったクライアント/サーバー型RDBMSが安価で使いやすくなった時期と重なる。したがって、スタンドアローン型データアクセスアプリケーション開発のみならず、クライアントツールとしてもVisualBasicは注目を浴びたのである。

ところが、VisualBasic 3.0が発売されなかった我が国では、そうはいかなかった。Jetが搭載されておらず、ODBCが現実的には使えなかったVisualBasic 2.0では、データアクセスアプリケーションとしても、クライアントツールとしても使えなかったのである。

そして、ついにVisualBasic 4.0が登場した。今までの不満をすべて解消してくれそうな内容であり、皆が待ち望んでいたものだ。今までしかたなく使いにくいAccess(2.0ではだいぶよくなったものの、これは初心者向けツールであり開発用ツールとは言い難い)でやっていた仕事も、VisualBasicで柔軟にプログラミングできるようになるのである。

●データアクセスの構造

さて、ではその内容だが、実際問題としてキチンと説明するには膨大すぎるし、今までの経緯をご存じの方に限定して説明するにはカンタンになりすぎる(というか、オブジェクトの構造がこうなったとかでほとんど終わってしまう)。というわけで、ここでは時系列も考慮に入れながらも、全体の概要を述べるというスタンスにしたい。

まずは、データアクセスでできるコトを、その構造から述べて行くことにしよう。

図は、VisualBasicやAccessからデータアクセス機能を使うときの概念図である。

・Jetエンジン

いずれも、アプリケーションからデータアクセスの機能を使うためには、必ずJetデータベースエンジンを経由する。このエンジンはDLLとして供給される。

Jetエンジンは、SQL文なども解釈できる能力を持っているが、エンジンとしてネイティブなのはISAMであり、Accessと同様.MDB形式のファイルが使われる。

・Installable ISAMs

Jet自体は.MDB形式を扱うのだが、XbaseやParadox、BtrieveといったISAM系のファイルにもアクセスできるようになっている。

・データアクセスオブジェクト(DAO)

VisualBasic 4.0から大きく変わっているのは、DLLながらOLE ServerすなわちIn-Process Serverになっている点である。VisualBasicから使うときには、OLE Serverにアクセスするワケだ。従来は、単にJet エンジンをDLLコールしていたわけだから、データアクセスに関する構文はVisualBasicの予約語になっていたのだが、今回からはすべてが外部にあるOLE Serverが供給するインターフェイスの言語仕様になっている。これに関係して、OLE Server側のメソッドやプロパティと予約語が重複しても大丈夫なようにインタープリタ自体もVBAになって改良されている。

図からも分かるように、Jetエンジンの一部としてDAOがある。これは、データアクセスオブジェクトと呼ばれるもので、実際にはOLEServerとしての言語仕様はこのDAOに負っている。つまり、DAOの持つ言語仕様を、そのままVisualBasicから呼び出して使うのである。ただし、Standard EditonからはDAOのインスタンスをコードで作成することはできない。

・ODBC

Jetエンジンにはコンテナアプリケーションへのインターフェイスを取るオブジェクトの言語仕様とデータベースエンジンが搭載されているわけだが、これらをうまく活かしてクライアント/サーバー型のRDBMSにアクセスしようというのが、ODBC(Open DataBase Connectivity)である。

正確には、ODBCはAPIの集合であり、あるアプリケーションからこの共通なAPIを通して外部の多種多様なデータソースにアクセスしようというものだ。特にVisualBasic用というわけではないのである。つまりはこれも標準化の一つなのである。

ODBCの構造(図)から分かるように、ODBCインターフェイスはアプリケーションからAPIを経由してODBCドライバマネージャへ到達する。ここから先は、どんなデータソースにアクセスするにしてもODBCドライバが必要である。ODBCドライバは、Oracle 7.1用、SQL Server用といったクライアント/サーバー型RDBMSに接続するためのものでもよいし、AccessやParadox、さらにはExcelやCSV形式のファイルにも対応するものがある。このように、どんな外部ソースに対しても、共通のAPIでアクセスしようというものなのだ。

ODBCドライバマネージャをインストールすると、コントロールパネルにODBCというアイコンが登録される。これを実行すると図のようなアプレットが起動され、データソースに対応するドライバを登録して、Alias名やパラメータなどを指定することができる。

 

このように、ODBCはJetとは独立した仕組みであり、ODBCのAPIをコールすれことができるものならば、どんなアプリケーションからも使うことができる。しかし、Jetエンジンと組み合わせて使えば、まるでAccessの.MDBファイルを使うかのごとく、外部のデータソースにアクセスすることができるのだ。細かいことを記述しなくてはいけないODBC APIではなく、高機能なDAOを使うことができるのは大きなメリットである。しかし、忘れてはいけないのは、高機能ゆえオーバーヘッドやクセもあるということだ。

●データコントロールとバウンダリーコントロール

データアクセスのコードを書いて、検索結果を得た変数から、フィールドに対応した特定のコントロールにデータを表示する。また、その逆というのが、データアクセスアプリケーションで必ずある処理だ。そういう決まりきった処理ならば、もっとカンタンにできるような仕組みがあってもいいハズだ。

それが、データコントロールとバウンダリーコントロール(データ認識コントロール)である。図は、VisualBasic 4.0のツールボックスだ。下から4段目の右側にあるのが、データコントロールである。このコントロールはConnect、DatabaseName、RecordSourceといったプロパティがあり、特定のデータベースファイルあるいはODBCのデータソース中にある特定のテーブルやクエリー結果を指定し、そのレコードセットを得ることができるものだ。いちいちコーディングしなくて済むのは楽だが、逆にStandard Editonからデータベースを扱うにはこのおおざっぱな方法しか手段がない。 aaa

それが、データコントロールとバウンダリーコントロール(データ認識コントロール)である。図は、VisualBasic 4.0のツールボックスだ。下から4段目の右側にあるのが、データコントロールである。このコントロールはConnect、DatabaseName、RecordSourceといったプロパティがあり、特定のデータベースファイルあるいはODBCのデータソース中にある特定のテーブルやクエリー結果を指定し、そのレコードセットを得ることができるものだ。いちいちコーディングしなくて済むのは楽だが、逆にStandard Editonからデータベースを扱うにはこのおおざっぱな方法しか手段がない。

VisualBasic 4.0から追加されたプロパティとして、BOFActionとRecordsetTypeがある。BOFActionは、BOFやEOF時に特殊な動作をした以前のものから、使いやすい動作も選べるように改良されたものである。RecordsetTypeは、Table型、Dynaset型、Snapshot型を指定できるようにしたものである。従来あったこれらのオブジェクトは、VisualBasic 4.0からはRecordset型に統一されており、そのタイプとして3つの型ができたのである。ただし、従来のコードもそのまま走るようにはなっている。

データコントロールだけでも有用ではあるが、先に述べたようなコトを実現するためには、バウンダリーコントロールを使う。これはそういう名前のコントロールがあるわけではなく、データコントロールとセットで使うコントロールの総称である。バウンダリーコントロールには、テキストボックスやラベル、チェックボックス、リストボックス、コンボボックス、さらにはピクチャーボックスまでもがある。

これらのコントロールを使えば、レコードの移動に合わせて自動的に指定したフィールドの内容が表示されるアプリケーションが、プロパティの指定だけでできてしまう。もちろん、データの更新も自動的に行われる。図は、VisualBasic 4.0付属のアドインであるデータフォームデザイナで作成したメンテナンスプログラムである。削除などができるボタンまでもが作られている。

アドインのデータフォームデザイナで作成したメンテナンスプログラム。

データコントロールとバウンダリーコントロールの組み合わせだ。すべて手動で作成してもたいした手間ではない。

 

 

 

 

 

さらに、データアクセスに特化したコントロールがOCXとして供給されている。これには、DB Grid、DB ListBox、DB Combobox、DB Outlineなどのコントロールがある。

DBGRIDには専用のプロパティがあり、テーブルの内容をすべて表形式で表示、編集することができる(図)。

 

 

 

 

 

 

 

 

 

データコントロールとDBGRIDがあれば、こんなアプリケーションを作るのに30秒とかからない。

 

DBLISTとDBCOMBO(図)では、指定したフィールドの一覧をリストボックスやコンボボックスの形式で表示することができる。

 

 

 

 

 

 

 

 

DBLISTDBCOMBO。指定したフィールドの一覧をリストボックスやコンボボックスの形式で表示することができる。

 

また、ODBCとの組み合わせで、外部にあるRDBMS`サーバーにアクセスすることも可能である(図)。

 

RecordSourceプロパティの設定

 

Oracle7 Serverのデータもごらんの通りスグに取ってこられる。

 

 

 

 

 

 

このように、効率的に実用的なプログラミングできるような配慮がなされている。

●DAOの概要

VisualBasic 4.0はStandard Edition、Professional Editionともに、DBEngine オブジェクトを起点とするデータ アクセス オブジェクト (DAO) をサポートしている。このオブジェクトモデルは、Access 1.0、Access 1.1、VisualBasic 3.0、Access 2.0と進歩してきたものである。図は、Jet 1.1すなわちVisualBasic 3.0のDAOモデルと、Jet 2.5すなわちVisualBasic 4.0のDAOモデルである。ここには大幅な仕様の追加が行われていることが分かるだろう。

ここでは、各オブジェクトの概要と、オブジェクトに属するプロパティ、メソッドなどについて表にまとめた。

★DAO 2.5のオブジェクトの概要

オブジェクト コレクション 説明
DBEngine Jet データベースエンジンを表す
Workspace Workspaces ユーザーのセッション
Database Databases 物理的なデータベースを表す論理的オブジェクト
TableDef TableDefs テーブルの構造
QueryDef QueryDefs クエリーの定義
Recordset Recordsets テーブルまたはクエリーの結果(読み書き可能型とリードオンリー型)
Releation Relations クエリーやテーブルのフィールドの関係
Container Containers コンテナに含まれるオブジェクトの情報
Document Documents Database、Table、QueryDef、Relationオブジェクトのいずれかの一つのインスタンスについての情報
Field Fields レコードに含まれるカラム
Index Indexes 高速アクセス用インデックス
Paramater Paramaters パラメータクエリーで作成されたQueryDefについてのパラメータ
User Users アクセスする権限を持っているユーザーアカウント
Group Groups ユーザーアカウントのグループ
Error Errors データアクセスエラーに関する詳細情報

 

★DAO 2.5 オブジェクトのプロパティやメソッドについては、 daoobjs.docを参照してください。

大きく変わっているのは、次の点だろう。

このように、クライアントツールとして現実的な機能アップがなされているのは、自体に即したものであり、VisualBasicユーザーにとっては嬉しいことだ。

次に、実際にこれらの仕様変更によって、以前のコードをどう書き換えたらよいかの例を示す。ただし、基本的には上位コンパチを保っているので、従来のコードもそのまま走る。

★Jet データベース エンジン Version 2.5 へのコードの変換例

※この例はヘルプからの抜粋です。あった方が親切かなと思いましたが、VisualBasic 3.0を使ったことの内皮とには意味がないので、なくてもいいような気がします。一応入れておきます。

データベースを開く場合

1.x Set MyDb = OpenDatabase("BIBLIO.MDB")

2.5 Set MyDb = DBEngine.Workspaces(0).OpenDatabase("BIBLIO.MDB")

または Set MyDb = Workspaces(0).OpenDatabase("BIBLIO.MDB")

テーブルを開く場合

1.x Dim MyTable As Table

Set MyTable = MyDB.OpenTable("Titles")

2.5 Dim MyTable As Recordset

Set MyTable = MyDB.OpenRecordset("Titles", dbOpenTable)

または Dim MyTable As Recordset

Set MyTable = MyDB!Titles.OpenRecordset()

排他的なテーブルを開く場合

1.x Dim MyTable As Table

Set MyTable = MyDB.OpenTable("Titles", DB_DENYREAD)

2.5 Dim MyTable As Recordset

Set MyTable = MyDB.OpenRecordset("Titles", dbOpenTable, dbDenyRead)

または Dim MyTable As Recordset

Set MyTable = MyDB!Titles.OpenRecordset(dbOpenTable, dbDenyRead)

ダイナセットを開く場合

1.x Dim MyDynaset As Dynaset

Set MyDynaset = MyDB.CreateDynaset("Titles")

2.5 Dim MyDynaset As Recordset

Set MyDynaset = MyDB.OpenRecordset("Titles", dbOpenDynaset)

または Set MyDynaset = MyDB!Titles.OpenRecordset(dbOpenDynaset)

スナップショットを開く場合

1.x Dim MySnapshot As Snapshot

Set MySnapshot = MyDB.CreateSnapshot("Titles")

2.5 Dim MySnapshot As Recordset

Set MySnapshot = MyDB.OpenRecordset("Titles", dbOpenSnapshot)

または Set MySnapshot = MyDB!Titles.OpenRecordset(dbOpenSnapshot)

フィールドを一覧表示する場合 (ListFields メソッドを書き換えます)

1.x ' Table オブジェクト MyTable が開かれている場合。


Dim ListSet As Snapshot
Set ListSet = MyTable.ListFields()
While Not ListSet.EOF
  Debug.Print ListSet!Name, ListSet!Type
  ListSet.MoveNext
Wend
ListSet.Close

または ' Database オブジェクト Db が開かれている場合。


Dim i%, j%, Td As TableDef
For j% = 0 To Db.TableDefs.Count - 1
        Set Td = Db.TableDefs(j%)
        Debug.Print "Table Name:"; Td.Name
        For i = 0 To Td.Fields.Count - 1
            Debug.Print Td.Fields(i).Name;
            Debug.Print Td.Fields(i).Type
        Next I
Next j%

2.5	' Recordsetオブジェクト MyTableが開かれている場合

Dim Fld as Field
For Each Fld in MyTable.Fields
  Debug.Print Fld.Name, Fld.Type
Next Fld

または ' Table オブジェクトではないテーブル Table1 がある場合。


Dim I As Integer, Dim DB As Database
Set DB = Workspaces(0).OpenDatabase("MyDb.MDB")
For I = 0 To DB!Table1.Fields.Count - 1
  Debug.Print DB!Table1.Fields(I).Name
Next I

データベース内のテーブルを一覧表示する場合 (ListTables メソッドを書き換えます)

1.x ' Database オブジェクト MyDB が開かれている場合。


Dim ListSet As Snapshot
Set ListSet = MyDB.ListTables()
While Not ListSet.EOF
  Debug.Print ListSet!Name
  ListSet.MoveNext
Wend
ListSet.Close

または ' Database オブジェクト Db が開かれている場合。


Dim i%, j%, Td As TableDef
For j% = 0 To Db.TableDefs.Count - 1
        Set Td = Db.TableDefs(j%)
        Debug.Print "Table Name:"; Td.Name
Next j%

2.5 ' Database オブジェクト MyDB が開かれている場合。


Dim Td as TableDef
For Each Td in MyTable.TableDefs
  Debug.Print Td.Name
Next Td

または ' QueryDefs コレクションにクエリーが含まれている場合。


Dim J As Integer
For J = 0 To MyDB.QueryDefs.Count - 1
  Debug.Print MyDB.QueryDefs(J).Name
Next J

テーブルのインデックスを一覧表示する場合 (ListIndexes メソッドを書き換えます)

1.x ' Table オブジェクト MyTable が開かれている場合。


Dim ListSet As Snapshot
Set ListSet = MyTable.ListIndexes()
While Not ListSet.EOF
  Debug.Print ListSet!Name
  ListSet.MoveNext
Wend
ListSet.Close

または ' Database オブジェクト Db が開かれている場合。


Dim i%, j%, Td As TableDef
For j% = 0 To Db.TableDefs.Count - 1
        Set Td = Db.TableDefs(j%)
        Debug.Print "Table Name:"; Td.Name
        For i = 0 To Td.Indexes.Count - 1
		Debug.Print Td.Indexes(i).Name;
        Next I
Next j%

2.5 ' Recordset オブジェクト MyTable が開かれている場合。


Dim I As Integer, Idx as Index
For Each Idx in MyTable.Indexes
  Debug.Print Idx.Name
Next I

クエリーのパラメータを一覧表示する場合 (ListParameters メソッドを書き換えます)

1.x ' QueryDef オブジェクト MyQuery が開かれている場合。


Dim ListSet As Snapshot
Set ListSet = MyQuery.ListParameters()
While Not ListSet.EOF
  Debug.Print ListSet!Name
  ListSet.MoveNext
Wend
ListSet.Close

2.5 ' QueryDef オブジェクト MyQuery が開かれている場合。


Dim I As Integer, Qp as Parameter
For Each Qp in MyQuery.Parameters
  Debug.Print Qp.Name
Next I


クエリーのパラメータを動的に設定する場合

1.x ' データベース Db が開かれている場合。


Dim Qd as Querydef, Ds as Dynaset
Set Qd = Db.OpenQueryDef("MyQuery")
Qd.ParameterName = "parm"
Set Ds = Qd.CreateDynaset()

2.5 ' QueryDef オブジェクト MyQuery が開かれている場合。


Dim I As Integer
For I = 0 To MyQuery.Parameters.Count - 1
  MyQuery.Parameters(I) = InputBox("Value for " & MyQuery.Parameters(I).Name)
Next I

または Dim I As Integer

For I = 0 To MyQuery.Parameters.Count - 1
  MyQuery.Parameters(I) = InputBox("Value for " & MyQuery(I).Name)
Next I

パススルー クエリーを実行する場合

1.x ' Database オブジェクト MyDb が開かれている場合。

Dim Rows as Long

Rows = MyDb.ExecuteSQL("EXECUTE SP_MySp")

または Dim Sn as Snapshot

Set Sn = Db.CreateSnapshot("EXECUTE SP_MySp", DB_SQLPassThrough)

2.5 ' Database オブジェクト MyDb が開かれている場合。

Dim Q As QueryDef Set Q = MyDb.CreateQueryDef("MYODbCQuery") Q.Connect = "ODBC;" _ & "DSN=MyServer;" _ & "UID=sa;" _ & "PWD=hithere;" _ & "DATABASE=pubs" Q.SQL = "Exec SP_MySp" Q.ReturnsRows = True ... Dim R As Recordset Set R = Q.OpenRecordset()

一時的なクエリーを作成する場合

1.x ' Database オブジェクト MyDb が開かれている場合。


Dim TempQ As QueryDef
Dim TempQName As String
TempQName = "TMP:" & User() & Time()
TempQ = MyDb.CreateQueryDef(TempQName)
TempQ.SQL = "Update Table1 Set Table1.Field1 = Field1 * 1.1;"
...
TempQ.Execute
TempQ.Close
...
MyDb.DeleteQueryDef(TempQName)

2.5 ' Database オブジェクト MyDb が開かれている場合。


Dim TempQ As QueryDef
Set TempQ = MyDb.CreateQueryDef("", "Update Table1 Set Table1.Field1 = 
Field1 * 1.1;")
...
TempQ.Execute
TempQ.Close

●データマネージャ

VisualBasicには、データベースのテーブル作成などに威力を発揮するデータマネージャが搭載されている。これは別アプリケーションになっていて、VisualBasicから呼び出すようになっている。

VisualBasic 3.0のデータマネージャはテーブルやインデックスの定義をする機能しかなかったが、VisualBasic 4.0のデータマネージャでは、従来の機能に加えて、リレーションの作成などのデータベースの構造を定義することが、より使いやすいインターフェイスでできる(図)ほか、データのメンテナンスや検索などもできる。さらには、ODBCのアタッチテーブルの作成も可能である。

 

 

 

 

 

 

 

 

 

 

データマネージャでは、データベースやテーブルやインデックス、リレーションの作成など、データベースの構造を定義することができる

 

 

 

 

 

 

 

 

 

 

データマネージャでは、データのメンテナンスや検索もできる。

 

 

 

 

 

 

 

 

 

アタッチテーブルの作成

従来ならば、メンテナンス用にもAccessがあった方がいいと述べてきたのだが、今度のデータマネージャがあれば、Accessはもう必須ではないだろう。そして驚くべきことに、このデータマネージャ自体がVisualBasic4.0で書かれている。それだけの機能がVisualBasicには備わっているのである。

●VBSQLカスタムコントロールとODBC API

このように、DAOやデータコントロールを使ったプログラミングでは、比較的カンタンにクライアント/サーバー型のデータアクセスアプリケーションを作成することができる。しかし、前に述べたように、オーバーヘッドが大きくパフォーマンスが悪いといった問題も指摘されてきた。もちろん、Microsoftとしてもこの事実は分かっており、パフォーマンスもチューニングされてきているようだ。しかし、Jetエンジンを経由する構造上、最高の速度とはなり得ないのはいたしかたない。

そこで、MicrosoftはSQL Server用に専用のミドルウェアを用意した。それが、VBSQLカスタムコントロールである。VBSQL カスタムコントロールを使うには、宣言ファイル、ドライバ、および DLL を追加する必要がある。これらのファイルは Visual Basic には含まれていないが、Microsoftから別途入手することができるという。

しかし、ODBCというオープンな規格を推進しているMicrosoftが、自社製品用に高速なインターフェイスを別売するというのは、どうもいただけない。

とはいえ、これでMicrosoft SQL Serverは高速化できる。しかし、他のベンダーのサーバー、たとえばOracleなどはそうはいかない。もちろん、ミドルウェアとしてODBCを選択しないとなれば、もっとたくさんの選択肢があるわけだが、どうしてもODBCを使いたいというガンコな方には、ODBC APIを直接コールするという方法もある。

これには、ODBC SDKなどを入手すれば、APIの概要が分かる。ODBC API 宣言ファイルも、Microsoftから入手可能だと言う。

このような方法で高速化をすることはできるのだが、当然ながらDAOの持つ柔軟なプログラミング環境のメリットを活かすことはできない。高速だけを目指すならば、最初からVisualBasicではなくVisualC++などのツールを使うべきなのかもしれない。

●まとめ

VisualBasic 4.0のデータアクセス機能は、クライアント/サーバー環境に合わせて大きく拡張してあり、非常に好感が持てた。また、従来は言語仕様に含まれていたため、何かと面倒なことになったDAOがIn-Process OLE Serverとなったことも、Microsoftの押し進めるオブジェクト指向OSの歴史上、大きなトピックとなるだろう。

業務アプリケーションのベースはデータベースである。今後、VisualBasicがデータベースアプリケーション開発ツールの中心となっていくことになるだろう。