こんにちは!Access VBAの世界へようこそ。
マクロの記録から一歩踏み出し、「自分でプログラムを書いて業務を自動化したい!」という熱意を持つあなたへ、今日はとてもエキサイティングなお話をしますね。
Accessでデータベースを操作するとき、通常はDAO(Data Access Objects)という仕組みを使います。テーブルを作ったり、フィールドを追加したりするのに非常に便利な標準機能です。
しかし、実務で何万件ものデータを扱う大規模なシステムを作ったり、複数のユーザーが同時にアクセスする環境を構築したりすると、DAOだけでは「なぜかテーブルがロックされて動かなくなった」「メモリが解放されなくてAccessが重くなった」という、プロ泣かせの壁にぶぶつかることがあります。
今回は、そんなDAOの限界を突破し、Windowsの底力(API)を借りてテーブル定義の操作を極限まで安定させるプロの技を、優しく丁寧にお伝えします。ここをクリアすれば、あなたのAccess VBAのスキルは間違いなく中級者から「プロの領域」へとランクアップしますよ!
—
1. なぜDAOのテーブル定義操作で限界が来るのか?
まずは、私たちが普段何気なく使っているDAOの裏側の話を少しだけさせてください。
DAOを使ってVBAでテーブルを作るとき、コードはこんなふうになりますよね。
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
Set db = CurrentDb
Set tdf = db.CreateTableDef(“T_顧客マスタ”)
‘ フィールドの追加
Set fld = tdf.CreateField(“顧客ID”, dbLong)
tdf.Fields.Append fld
db.TableDefs.Append tdf
一見、とてもシンプルで分かりやすいコードです。
しかし、このコードには「Accessのエンジン(ACE/Jet)が内部で裏方仕事を抱え込みやすい」という弱点があります。
- 排他制御(ロック)の競合: テーブル構造を変更(TableDefの追加や変更)する瞬間、Accessはデータベース全体、あるいは該当オブジェクトに強いロックをかけます。ネットワーク経由(共有フォルダ)でこの処理を行うと、タイミングによって「他のユーザーが使用中」エラーが容赦なく飛んできます。
- メモリリークの温床: `Set tdf = Nothing` や `Set db = Nothing` を正しく記述してオブジェクトを解放しないと、Accessのメモリ内にゴーストのようなゴミデータが残り、動作が不安定になります(最悪の場合、mdb/accdbファイルが破損します)。
「じゃあ、どうすればいいの?」
そこで登場するのが、SQLのDDL(Data Definition Language)の実行です。実は、DAOのオブジェクトをゴリゴリ操作するよりも、純粋なSQL文をAccessのエンジンに直接「一撃」で投げ込んだ方が、圧倒的に安全で速いのです。
—
2. 【基本】DAOオブジェクト操作からSQL(DDL)への脱却
まずは、プログラミング初学者の方に向けて、「オブジェクトを1つずつ捕まえて操作する方法」から、「SQLという命令文を一発投げる方法」への切り替え方をマスターしましょう。
悪い例(DAOオブジェクトの多重操作)
オブジェクトの生成と追加を細かく行うため、コードが長くなり、エラーが起きたときの復旧が難しくなります。
良い例(SQLのDDL文を使う方法)
`CurrentDb.Execute` を使って、SQLで「テーブルを作れ!」と命令します。
Sub CreateTable_Smartly()
Dim strSQL As String
On Error GoTo ErrorHandler
‘ SQLでテーブル定義を丸ごと指定して作成する
strSQL = “CREATE TABLE T_顧客マスタ (” & _
“顧客ID LONG CONSTRAINT PrimaryKey PRIMARY KEY, ” & _
“顧客名 TEXT(50) NOT NULL, ” & _
“登録日 DATETIME” & _
“);”
‘ 実行
CurrentDb.Execute strSQL, dbFailOnError
MsgBox “テーブルの作成が正常に完了しました!”, vbInformation
Exit Sub
ErrorHandler:
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical
End Sub
ここがポイント!
`dbFailOnError` というオプションをつけるのがプロの流儀です。途中でエラー(例えば、すでに同じ名前のテーブルがあるなど)が起きたとき、中途半端な状態でテーブルを作らず、「すべてを無かったこと(ロールバック)」にしてくれます。データ破損を防ぐための必須テクニックです。
—
3. 【極限の安定化】APIレベルの思考とファイルシステムの同期
さて、ここからが本題です。さらに大規模なシステムや、外部からAccessファイルをバッチ処理で操作するような現場では、「Accessが裏でファイル書き込みを完了したかどうか」を確信できないという問題に直面します。
VBAでコードを走らせて「よし、テーブルを作ったぞ!」と次の行に進んでも、WindowsのOSレベルでは、ハードディスク(SSD)への書き込みがまだキャッシュ(一時領域)に残っている場合があります。この状態で強制終了したり、続けて外部プログラムからファイルを触ったりすると、ファイルが破損します。
これを完全に制御するため、Windowsのシステム機能(API)の概念を取り入れます。
大げさなAPI関数を宣言しなくても、「ファイルシステムの同期とセッションの確実なリフレッシュ」を意識するだけで、安定性は劇的に向上します。
実務で使える!極限安定版・テーブル定義変更モジュール
以下のコードは、既存のテーブルに対して安全にフィールドを追加し、確実にディスクへ書き込みを行わせる実践的なプロシージャです。
Public Sub SafeAddColumnToTable(ByVal tableName As String, ByVal columnName As String, ByVal columnType As String)
Dim db As DAO.Database
Dim strSQL As String
‘ トランザクションとエラーハンドリングの準備
On Error GoTo SafeError
‘ データベース変数の取得(CurrentDbは都度呼び出すと不安定になるため変数に保持)
Set db = CurrentDb
‘ 1. すでにフィールドが存在するかチェック(二重実行を防ぐプロの配慮)
If CheckFieldExists(db, tableName, columnName) Then
MsgBox “指定されたフィールドは既に存在します。”, vbExclamation, “スキップ”
GoTo SafeExit
End If
‘ 2. SQL (DDL) による安全なフィールド追加
strSQL = “ALTER TABLE ” & tableName & ” ADD COLUMN ” & columnName & ” ” & columnType & “;”
‘ データベースエンジンに直接実行を命じる
db.Execute strSQL, dbFailOnError
‘ 3. キャッシュのフラッシュとデザイン変更の強制同期
‘ TableDefsコレクションを最新の状態に強制更新し、OSへの書き込みを促す
db.TableDefs(tableName).Refresh
‘ 4. Accessの内部キャッシュを最適化(Compactの準備やメモリ解放)
DBEngine.Idle dbRefreshCache
MsgBox “テーブル定義の変更が安全に完了しました。”, vbInformation
SafeExit:
‘ オブジェクトの確実な解放(メモリリーク防止)
Set db = Nothing
Exit Sub
SafeError:
MsgBox “予期せぬエラー: ” & Err.Number & vbCrLf & Err.Description, vbCritical, “致命的エラー”
Resume SafeExit
End Sub
‘ — 補助関数:フィールドが存在するかをチェックする —
Private Function CheckFieldExists(db As DAO.Database, tableName As String, fieldName As String) As Boolean
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
Dim exists As Boolean
exists = False
Set tdf = db.TableDefs(tableName)
For Each fld In tdf.Fields
If LCase(fld.Name) = LCase(fieldName) Then
exists = True
Exit For
End If
Next fld
CheckFieldExists = exists
End Function
このコードの何が「プロ仕様」なのか?
1. `DBEngine.Idle dbRefreshCache` の使用:
これが今回の隠し味です。Access(Jet/ACEエンジン)に対して「ちょっと手が空いたら内部キャッシュをきれいにして、確実にハードディスクと同期してね」と明示的に指示を出しています。これにより、VBAが終了した直後に別の処理が走っても、ファイル競合が起きにくくなります。
2. `CurrentDb` の変数保持:
`CurrentDb` は呼び出すたびに新しいデータベースオブジェクトのインスタンスを作ります。条件分岐の中で何度も `CurrentDb.TableDefs…` と書くと、メモリ管理がめちゃくちゃになります。最初に `Set db = CurrentDb` で捕まえて使い回すのが鉄則です。
3. 二重実行チェック(防御的プログラミング):
すでに存在するフィールドを追加しようとしてエラーになるのを未然に防ぐため、事前にチェックを入れています。
—
まとめ:ここをクリアすれば、Access VBAの基本はバッチリ!
いかがでしたでしょうか?
今回は、DAOの限界を知り、SQL(DDL)の活用や、`DBEngine.Idle` によるシステムレベルの同期といった「一歩進んだ安定化テクニック」をご紹介しました。
- オブジェクトをチマチマ操作するのではなく、SQL(DDL)でドンと命令する。
- `CurrentDb` は変数に格納して使い回し、最後に必ず `Set = Nothing` で解放する。
- `DBEngine.Idle dbRefreshCache` でOSとAccessのメモリを確実に同期させる。
この3つを意識するだけで、あなたが作るAccessシステムは、エラーとは無縁の「ビクともしない堅牢なアプリ」へと生まれ変わります。
最初は難しく感じるかもしれませんが、現場で「動かない…」と頭を抱える夜をなくすための、最強の武器になります。ぜひ今日のコードをあなたの開発環境にコピーして、試してみてくださいね。
あなたのAccess VBAライフが、より知的で快適なものになりますように。次のステップへ進む準備はバッチリですよ!
