Access開発における「テーブル存在チェック」のパラダイムシフト
現場のAccess開発で、最も無駄なデバッグ工数を奪う原因の一つを知っているか?
それは、「存在しないテーブルやクエリにアクセスして走る実行時エラー(トラップ可能なエラー 3078)」だ。
「とりあえずエラーハンドリング(`On Error Resume Next`)を入れておけばいいや」
――もし君がそう考えているなら、今すぐその甘い設計を捨ててほしい。
エラーを発生させてから `Err.Number = 3078` をキャッチするアプローチは、VBAの実行コンテキストにおいて極めて重いオーバーヘッドを伴う。さらに、予期せぬエラーまで握りつぶしてしまうリスクを孕んだ、極めて場当たり的なコーディングだ。
プロフェッショナルなエンジニアが目指すべきは、エラーを「事後処理」することではなく、「事前に防ぐ(防御的プログラミング)」ことである。
今回は、`Application.CurrentData.AllTables` コレクションを駆使し、Accessの内部メタデータを一瞬で参照してスマートに分岐する、極限まで洗練されたテーブル存在確認のアーキテクチャを伝授する。
—
なぜ `CurrentDb.TableDefs` ではなく `CurrentData.AllTables` なのか?
Access VBAでテーブルの存在を確認する際、古い教科書やネットのサンプルでは `CurrentDb.TableDefs` をループさせたり、存在しない名前を指定してエラーをトラップする方法が紹介されている。
しかし、これは以下の理由から現代のエンタープライズ開発ではアンチパターンだ。
1. パフォーマンスの劣化: `TableDefs` はDAO(Data Access Objects)のコレクションであり、ローカルテーブルだけでなくリンクテーブルやシステムテーブルの定義情報までメモリ上にロードするため、重い。
2. オブジェクトモデルの不一致: 現代のAccessはADO/ACEDAOが混在する環境であり、ナビゲーションウインドウ(UI)に表示されるテーブル群の状態をストレートに取得するには、AcObjectTypeに対応した `CurrentData` オブジェクトを使うのが最もオブジェクト指向的かつ軽量である。
`Application.CurrentData.AllTables` は、Accessデータベース内のすべてのテーブル(ローカルおよびリンク)のメタデータを格納するコレクションだ。これを利用すれば、データベースエンジンに負荷をかけず、メモリ上でミリ秒単位のテーブル存在チェックが可能となる。
—
プロダクションコード:堅牢なテーブル存在チェック関数
現場でそのままコピー&ペーストして使える、モジュール化された汎用関数を公開する。
単に存在有無を返すだけでなく、リンク切れや予期せぬ例外に対するガードも組み込んだ、実戦仕様のコードだ。
Option Compare Database
Option Explicit
‘ =================================================================================
‘ 模块名: basDatabaseUtility
‘ 概要 : データベースオブジェクトの存在確認および安全性確保に関するユーティリティ
‘ =================================================================================
/
- 指定されたテーブルがデータベース内に存在するかを高速に判定する
- @param ByVal strTableName As String – 存在を確認したいテーブル名
- @return Boolean – 存在する場合は True、存在しない場合は False
- @remarks Application.CurrentData.AllTables を使用し、エラー発生を伴わない軽量な判定を行う
/
Public Function ExistsTable(ByVal strTableName As String) As Boolean
‘ 不正な引数(空文字など)の早期リターン(ガード cláusula)
If Trim(strTableNamevbNullString) = vbNullString Then
ExistsTable = False
Exit Function
End If
Dim objTable As AccessObject
Dim bResult As Boolean
bResult = False
‘ On Error GoTo を使わない、モダンでクリーンなコレクション探索
On Error GoTo ErrorHandler
For Each objTable In Application.CurrentData.AllTables
If StrComp(objTable.Name, strTableName, vbTextCompare) = 0 Then
bResult = True
Exit For
End If
Next objTable
ExistsTable = bResult
Exit Function
ErrorHandler:
‘ 予期せぬ環境要因によるエラーはログに落とし、安全側に倒してFalseを返す
Debug.Print “[Error] ExistsTable: ” & Err.Description
ExistsTable = False
End Function
—
実務での活用シナリオ:動的インポート・エクスポート処理
この `ExistsTable` 関数が真価を発揮するのは、外部からのデータインポートや、一時テーブル(TempTable)を動的に生成・破棄するバッチ処理の現場だ。
以下のコードを見てほしい。存在しないテーブルをいきなりクエリやDAOで叩くのではなく、「処理の分岐をあらかじめコントロールする」ことで、メンテナンス性が劇的に向上する。
/
- 外部CSVのインポート処理(堅牢性重視モデル)
/
Public Sub ExecuteDataImportBatch()
Const TARGET_TABLE As String = “W_Temp_ImportData”
‘ 1. 一時テーブルが存在しない場合は、動的に作成する
If Not ExistsTable(TARGET_TABLE) Then
Call CreateTempImportTable(TARGET_TABLE)
Else
‘ 存在する場合は、前回のゴミデータをクリアするため一度全削除するか、再構築する
CurrentDb.Execute “DELETE FROM ” & QUOTENAME(TARGET_TABLE), dbFailOnError
End If
‘ 2. インポート処理本体へ進む
‘ DoCmd.TransferText … などの処理
MsgBox “インポートの準備が完了しました。”, vbInformation, “システム通知”
End Sub
/
- テーブル動的生成のヘルパー
/
Private Sub CreateTempImportTable(ByVal strTableName As String)
Dim strSQL As String
‘ DDLを使用して安全にテーブルを定義
strSQL = “CREATE TABLE ” & strTableName & ” (” & _
“ID COUNTER CONSTRAINT PrimaryKey PRIMARY KEY, ” & _
“ImportDate DATETIME, ” & _
“DataValue TEXT(255)” & _
“);”
CurrentDb.Execute strSQL, dbFailOnError
End Sub
/
- 識別子のクォート処理(SQLインジェクションや予約語対策の基本)
/
Private Function QUOTENAME(ByVal strName As String) As String
QUOTENAME = “[” & Replace(strName, “]”, “]]”) & “]”
End Function
—
チーフアーキテクトからの提言
AccessVBAの開発現場では、「動けばいいや」という場当たり的なコードが技術的負債として積み重なり、のちにシステム全体の崩壊を招くケースを嫌というほど見てきた。
今回紹介した `Application.CurrentData.AllTables` を活用した存在チェックは、コードの行数こそわずかだが、「エラーを発生させずに予期せぬ状態をハンドリングする」というプロフェッショナルな設計思想そのものだ。
無駄なエラーハンドリングを排除し、メタデータをスマートに読み解く。このアプリケーション構築アプローチを取り入れるだけで、君の書くAccess VBAのコードベースは見違えるほど堅牢で、保守性の高い「プロダクト」へと進化するはずだ。
