【入門編】【中級】DAO.Fieldオブジェクトでテーブル構造を動的に解析し、フィールド情報を列挙する – Access VBA解析バイブル

スポンサーリンク

こんにちは!Access VBAの世界へようこそ。チーフアーキテクトの私です。

マクロの記録ボタンを押すだけの世界から一歩踏み出し、「自分でプログラムを組み立てて業務を自動化したい!」そう思っているあなたへ。今回は、Access VBAの真骨頂とも言える「DAO(Data Access Objects)」を使った、テーブル構造の動的解析についてお話しします。

ここをクリアすれば、Access VBAの基本はバッチリですよ。ぜひ最後までついてきてくださいね。

なぜ「テーブル構造の動的解析」が必要なのか?

皆さんは、こんな経験はありませんか?

  • 「システム改修でテーブルに新しい列(フィールド)が追加されたら、VBAのプログラムがエラーを起こして止まってしまった…」
  • 「複数のテーブルからデータを集計したいけれど、テーブルごとに列の並びや有無が違ってハードコーディング(決め打ち)できない…」

初学者のうちは、プログラムの中に `Tbl.Fields(“氏名”)` のようにフィールド名を直接書き込みがちです。しかし、これではテーブルの設計変更(スキーマ変更)のたびに、プログラムを書き直す羽目になります。

ここで登場するのが、DAO(Data Access Objects)です。
DAOを使えば、プログラム側が「あらかじめテーブルの設計図を覗き見して、今どんなフィールドが存在するかを自動で把握する」という、柔軟でスマートな処理が実現できます。

アクセスオブジェクトモデルの全体像を掴もう

まずは、Accessがデータベースをどのように管理しているか、その「階層構造(オブジェクトモデル)」をイメージ図で見 てみましょう。

[Application] (Accessアプリ全体)
└── [CurrentDb()] (今開いているデータベース)
└── [TableDefs] (テーブル定義の集まり)
└── [TableDef] (個別のテーブル)
└── [Fields] (フィールドの集まり)
└── [Field] (個別のフィールド:名前やデータ型)

私たちがこれから行うのは、`CurrentDb` から `TableDefs` を経由して、テーブルの奥底にある `Fields` コレクションを覗き、一つひとつの `Field` が持つ「名前」や「データ型」を引っこ抜いてくる作業です。

【実践】テーブルの構造を動的に解析するVBAコード

百聞は一見に如かず。実際に、指定したテーブルの全フィールド名とデータ型をイミディエイトウィンドウに列挙するコードを見てみましょう。

以下のコードを、標準モジュールにそのままコピー&ペーストしてみてください。

Option Explicit

‘ =================================指定したテーブルの構造を解析するプロシージャ=================================
Sub AnalyzeTableStructure()
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
Dim targetTableName As String

‘ 解析したいテーブル名をここに指定します(実際のテーブル名に書き換えてください)
targetTableName = “T_社員マスター”

‘ 1. 現在のデータベースオブジェクトを取得
Set db = CurrentDb()

‘ 2. エラーハンドリング:指定したテーブルが存在するかチェック
On Error GoTo ErrorHandler
Set tdf = db.TableDefs(targetTableName)
On Error GoTo 0 ‘ エラー監視を解除

‘ 3. ヘッダー情報の出力
Debug.Print “========================================”
Debug.Print ” テーブル名: ” & tdf.Name
Debug.Print ” 総フィールド数: ” & tdf.Fields.Count
Debug.Print “========================================”

‘ 4. 各フィールドをループで総なめにする(ここが本質!)
For Each fld In tdf.Fields
Debug.Print ” [フィールド名]: ” & fld.Name & _
” / [データ型]: ” & GetDataTypeName(fld.Type) & _
” / [サイズ]: ” & fld.Size
Next fld

Debug.Print “========================================”

‘ 5. クリーンアップ(メモリ解放の作法)
Set fld = Nothing
Set tdf = Nothing
Set db = Nothing

MsgBox “テーブル解析が完了しました。イミディエイトウィンドウを確認してください。”, vbInformation
Exit Sub

ErrorHandler:
MsgBox “指定したテーブル ‘” & targetTableName & “‘ が見つかりません。”, vbCritical
Set db = Nothing
End Sub

‘ =================================DAOのデータ型(数値)を分かりやすい文字列に変換するヘルパー関数=================================
Function GetDataTypeName(varType As Integer) As String
Select Case varType
Case dbBoolean: GetDataTypeName = “Yes/No型 (Boolean)”
Case dbByte: GetDataTypeName = “バイト型 (Byte)”
Case dbInteger: GetDataTypeName = “整数型 (Integer)”
Case dbLong: GetDataTypeName = “長整数型 (Long)”
Case dbCurrency: GetDataTypeName = “通貨型 (Currency)”
Case dbSingle: GetDataTypeName = “単精度浮動小数点数型 (Single)”
Case dbDouble: GetDataTypeName = “倍精度浮動小数点数型 (Double)”
Case dbDate: GetDataTypeName = “日付/時刻型 (Date)”
Case dbText: GetDataTypeName = “短テキスト型 (Text)”
Case dbMemo: GetDataTypeName = “長テキスト型 (Memo)”
Case dbLongBinary: GetDataTypeName = “OLEオブジェクト型 (Blob)”
Case Else: GetDataTypeName = “その他・特殊型 (” & varType & “)”
End Select
End Function

コードの注目ポイントと知見の解説

チーフアーキテクトとして、このコードの重要なポイントをいくつか解説しておきます。ここを知っているかどうかで、エンジニアとしての格が一段上がります。

1. `CurrentDb()` は「呼び出すたびに新しいインスタンスが生成される」

`CurrentDb` は非常に便利なメソッドですが、裏側ではデータベースへの接続オブジェクトを新しく生成しています。
これをループの中で何度も呼び出すと、メモリリークやパフォーマンス低下の原因になります。今回のコードのように、最初に変数 `db` に格納して使い回すのが、プロフェッショナルの美しい作法です。

2. DAOの `Field.Type` は「定数(数値)」で返ってくる

`fld.Type` をそのまま表示させると、`3` とか `4` といった無機質な数字が返ってきてしまいます(これには私も最初は面食らいました)。
そのため、上記の `GetDataTypeName` 関数のように、DAOが用意している定数(`dbLong` や `dbText` など)と照らし合わせて人間が読める文字列に翻訳してあげるひと手間が必要です。

3. コレクションの巡回には `For Each … Next` を使う

`TableDef` の中にある `Fields` は「コレクション(集合体)」と呼ばれるオブジェクトです。
何番目にどのフィールドがあるか分からない動的な状態では、`For Each fld In tdf.Fields` のように記述することで、先頭から終わりまでスマートに安全に全フィールドを舐めることができます。

陥りやすい罠とエラーへの備え

実務でこのコードを組み込む際、初心者が必ずと言っていいほどハマる罠が「存在しないテーブル名やフィールド名を指定したときのエラー」です。

  • 対策: 必ず `On Error GoTo` を使ったエラーハンドリングを仕込みましょう。あるいは、以下のように `TableDefs` をループさせて存在チェックを行うのも安全なアプローチです。

‘ テーブルが存在するか事前にチェックする安全な書き方の一例
Function IsTableExists(tableName As String) As Boolean
Dim tdf As DAO.TableDef
On Error Resume Next
Set tdf = CurrentDb.TableDefs(tableName)
If Err.Number = 0 Then
IsTableExists = True
Else
IsTableExists = False
End If
On Error GoTo 0
End Function

おわりに

今回は、DAO.Fieldオブジェクトを使ってテーブル構造を動的に解析する方法を解説しました。

「テーブルの設計が変わってもビクともしない、メンテナンス性の高いシステム」を作るための第一歩が、このフィールド情報の列挙技術です。ここをマスターすれば、Access VBAの基本はもうバッチリと言って過望ありません。

ぜひご自身の開発環境でコードを動かして、イミディエイトウィンドウにズラリと並ぶフィールド情報を見てみてください。「お、プログラムがデータベースと対話しているぞ」というエンジニアとしての醍醐味を実感できるはずです。

それでは、次回の極限の知見でお会いしましょう。快適な自動化ライフを!

タイトルとURLをコピーしました