こんにちは!Access VBAの世界へようこそ。
長時間のバッチ処理を組んでいて、「最初はサクサク動いていたのに、処理が進むにつれてパソコンが重くなり、最終的に『リソース不足』や『メモリが足りません』で強制終了してしまう……」なんて悪夢を経験したことはありませんか?
もし心当たりがあるなら、それはあなたのコードが「オブジェクトのメモリリーク」を起こしているサインです。
今回は、プロの現場では常識中の常識である、DAOを使ったテーブル定義操作と「確実なクリーンアップ(後片付け)の作法」について、魂を込めてお伝えします。ここをクリアすれば、あなたの書くVBAは一段も二段もプロの領域に近づきますよ。それでは、一緒に本質を学んでいきましょう!
—
1. なぜメモリリークが起きるのか?(DAOの裏側で何が起きているか)
Access VBAでテーブルやフィールドをプログラムから操作するとき、私たちはDAO(Data Access Objects)というエンジンを使います。`TableDef`や`Field`といったオブジェクトをコード内で生成し、テーブルの構造をいじくるわけです。
ここで初心者がやりがちな「やってはいけない書き方」を見てみましょう。
‘ 【アンチパターン】絶対に真似してはいけないコード
Sub BadMemoryLeakExample()
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Set db = CurrentDb
‘ テーブルを作る
Set tdf = db.CreateTableDef(“TmpTable”)
tdf.Fields.Append tdf.CreateField(“ID”, dbLong)
db.TableDefs.Append tdf
‘ 処理が終わったのでそのまま終了……(これが大問題!)
End Sub
「あれ? エラーも出ないし、ちゃんとテーブルができるからいいじゃん」と思いましたか?
実はここに大きな罠があります。
VBAの世界では、`Set`キーワードを使ってオブジェクトを変数に代入した瞬間、Windowsのメモリ(ヒープ領域)上にそのオブジェクトの実体が確保されます。そして、処理が終わってプロシージャを抜けても、DAOやCOMコンポーネントの参照は、VBAのガベージコレクタが即座に完璧に回収してくれるとは限らないのです。
何千回、何万回とループするバッチ処理の中でこのコードを回すと、メモリ上に古いオブジェクトの残骸がどんどん溜まっていきます。これがメモリリークの正体です。
—
2. プロが実践する「使い捨てたら即解放」の鉄則
では、どうすればいいのでしょうか? 答えは非常にシンプルです。
「確保したオブジェクトは、使い終わったら責任を持って自分でメモリから消去(解放)する」これだけです。
オブジェクトを解放するには、変数を「Nothing」に設定します。
Set 変数名 = Nothing
これを行うことで、VBAに対して「もうこのオブジェクトは使わないので、メモリから解放していいですよ」と明確に伝えることができます。
解放する際の「正しい順番」
複数のオブジェクト(Database、TableDef、Fieldなど)を操作した場合は、生成した順番とは「逆の順序」で解放していくのが、メモリ管理における美学であり、トラブルを防ぐ絶対の作法です。
1. 末端のオブジェクト(Field、Indexなど)
2. 中間のオブジェクト(TableDef、QueryDefなど)
3. 根幹のオブジェクト(Database、Workspaceなど)
—
3. 【実践】メモリリークを完全封鎖する安全なテーブル作成コード
それでは、先ほどのコードを「プロ仕様のクリーンなコード」に書き換えてみましょう。
どんな長時間のバッチ処理であっても、リソースを1バイトもリークさせない鉄壁の構造がこちらです。
Sub ProTableCreation()
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
On Error GoTo ErrorHandler ‘ エラー時も確実に解放処理を通すための保険
‘ 1. データベースオブジェクトの取得
Set db = CurrentDb
‘ 2. TableDefオブジェクトの生成
Set tdf = db.CreateTableDef(“T_Mst_Processed”)
‘ 3. フィールドの生成と追加
Set fld = tdf.CreateField(“ID”, dbLong)
tdf.Fields.Append fld
Set fld = tdf.CreateField(“DataName”, dbText, 50)
tdf.Fields.Append fld
‘ 4. テーブルをデータベースのTableDefsコレクションに登録
db.TableDefs.Append tdf
MsgBox “テーブルの作成が正常に完了しました。”, vbInformation
CleanUp:
‘ ==========================================================
‘ 【極意】逆順でのオブジェクト解放(クリーンアップ)
‘ ==========================================================
‘ 生成したオブジェクトを確実にメモリから消去します
If Not fld Is Nothing Then Set fld = Nothing
If Not tdf Is Nothing Then Set tdf = Nothing
If Not db Is Nothing Then Set db = Nothing
Exit Sub
ErrorHandler:
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical
Resume CleanUp ‘ エラーが起きても必ず解放処理を通って終了する
End Sub
このコードの優れたポイント
- `On Error GoTo` によるトラップ: 万が一、処理の途中でエラー(同名テーブルがすでに存在する等)が発生しても、プログラムがそのまま止まらずに必ず `CleanUp` ラベルへジャンプし、メモリ解放を行ってから安全に終了します。
- 逆順の `Nothing` 代入: `fld` → `tdf` → `db` の順で、下位から上位へ確実にメモリを解放しています。
—
4. 陥りやすい罠:ループ処理での「インスタンスの使い回し」
テーブル定義の変更や、複数テーブルのフィールドを一括操作するバッチ処理では、ループの中でオブジェクトを何度も生成することがあります。
ここでもう一つ、プログラミング初学者がハマりやすい罠をご紹介します。
‘ 【やりがちなミス】ループ内で毎回新しいものを生み出し、古いものを放置する
Dim i As Long
For i = 1 to 100
Dim tdf As DAO.TableDef
Set tdf = db.CreateTableDef(“Table_” & i)
‘ 処理…
‘ 这里的解放を忘れていると、ループの回数分だけメモリリークが爆発的に増えます!
Next i
【正しいアプローチ】
ループ内でオブジェクト変数を再利用する場合は、次のループに入る前に必ず `Set 変数 = Nothing` を挟むか、ループのスコープ(生存期間)を意識して適切に破棄してください。また、可能であれば `Withブロック` や、1つのプロシージャ内での責務を小さく分割することが、メモリリークを防ぐ最大の防御策となります。
—
まとめ:ここをクリアすれば、Access VBAの基本はバッチリですよ!
お疲れ様でした!今回はDAOオブジェクトのメモリリークを徹底排除するためのクリーンアップ作法について解説しました。
- オブジェクトを生成したら、必ず `Set 〇〇 = Nothing` で解放する。
- 解放の順序は、生成した順序とは逆(末端から根幹へ)。
- `On Error` を活用し、例外発生時でも必ずクリーンアップを通る構造にする。
この作法を身につけたあなたなら、もう「長時間のバッチ処理で落ちるAccessマクロ」に怯える必要はありません。実務の現場で自信を持って、堅牢で美しいコードを書いていってくださいね。応援しています!
