止まらないAccessの「重さ」の正体:DAOオブジェクトを完璧に解放する「黄金の定型句」
こんにちは。Access VBAの世界へようこそ。
マクロの記録から一歩踏み出し、「自分でコードを書いて制御したい」と思うようになったあなたは、エンジニアとしての第一歩を力強く踏み出しています。素晴らしいですね。
しかし、コードを書き進めるうちに、こんな経験はありませんか?
「最初は速かったのに、使い続けるとAccessがどんどん重くなる」
「エラーが出ると、なぜかテーブルがロックされて編集できなくなる」
「数回実行しただけで、Accessが『応答なし』になって落ちてしまう」
もし心当たりがあるなら、あなたは今、「オブジェクトのライフサイクル(寿命)」という、プログラミングにおける最も重要で、かつ最も見落とされがちな壁に直面しています。
今日は、Access VBAの心臓部とも言える「DAO(Data Access Objects)」を扱う際に、プロが必ず守っている「メモリリークを防ぐためのオブジェクト破棄の作法」を伝授します。ここをマスターすれば、あなたの作るアプリケーションの安定性は劇的に向上します。
1. なぜ「使い終わった後」の処理が必要なのか?
VBAで `Recordset`(レコードセット)などのオブジェクトを扱うとき、私たちはコンピュータのメモリ(作業机のようなもの)に、一時的に「データの塊」を呼び出しています。
イメージしてみてください。
あなたは図書館(データベース)から、分厚い資料(Recordset)を借りて、自分の机(メモリ)に広げました。
- 正しい作法: 資料を読み終えたら、机を片付けて、資料を元の棚に戻す。
- NGな作法: 資料を読み終えたのに、机に広げっぱなしにする。
もし、あなたが何度も資料を借りて、机に広げっぱなしにしたらどうなるでしょう?
やがて机の上が資料で埋め尽くされ、新しい資料を広げるスペースがなくなります。これが「メモリリーク(メモリの漏洩)」です。
VBAにおいて、`Set rs = Nothing` と書くことは、単なるマナーではありません。「使い終わったメモリをOSに返却し、机を片付ける」という、システムを正常に動かし続けるための必須命令なのです。
2. 初心者が陥る「罠」:エラーが起きると片付けができない
「`Set rs = Nothing` を書いているから大丈夫」と思っている方、ちょっと待ってください。実はそこに最大の罠があります。
初心者が書きがちな、危ういコードを見てみましょう。
❌ 危険なコードの例
Sub BadExample()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Set db = CurrentDb
Set rs = db.OpenRecordset("SELECT * FROM T_売上データ")
' --- ここで何らかのエラーが発生したとする ---
' 例えば、データが想定外の形式だった場合など
Debug.Print rs!売上金額 * 1.1
' ------------------------------------------
' エラーが起きると、下の行まで辿り着けない!
rs.Close
Set rs = Nothing
Set db = Nothing
MsgBox "完了しました"
End Sub
何が問題か?
途中の計算処理でエラーが発生すると、VBAは即座に処理を中断してしまいます。すると、その下に書いてある `rs.Close` や `Set rs = Nothing` は実行されずにスキップされてしまいます。
結果として、レコードセットはメモリ上に「開きっぱなし」の状態となり、次にこのプログラムを動かそうとしたときには、メモリ不足やファイルロックの原因となってしまうのです。
3. プロの鉄則:エラーハンドリングと「クリーンアップ」の分離
では、どうすれば確実に「片付け」ができるのでしょうか?
プロのエンジニアは、「メインの処理」と「後片付け(クリーンアップ)」を明確に分離します。
これを実現するのが、`On Error GoTo` を使った「黄金の定型句」です。
✅ 黄金の定型句(プロフェッショナル・テンプレート)
以下の構造を、そのまま自分のコードの雛形(テンプレート)にしてください。
Sub ProfessionalRecordsetSample()
' 1. 変数の宣言(オブジェクトは必ずNothingで初期化しておくのが理想)
Dim db As DAO.Database
Dim rs As DAO.Recordset
' エラーが発生したら、エラー処理用のラベル(ErrorHandler)へ飛ぶように設定
On Error GoTo ErrorHandler
' 2. メインの処理
Set db = CurrentDb
Set rs = db.OpenRecordset("SELECT * FROM T_売上データ", dbOpenDynaset)
' レコードのループ処理(例)
Do Until rs.EOF
Debug.Print "売上ID: " & rs!売上ID & " / 金額: " & rs!売上金額
rs.MoveNext
Loop
' 正常終了した場合、クリーンアップ処理へ進むために、
' エラーハンドラを「飛び越える」ための命令を書く
GoTo CleanUp
ErrorHandler:
' 3. エラーが発生した場合の処理
' ここでは、エラー内容を報告するなどの処理を行う
MsgBox "エラーが発生しました。" & vbCrLf & _
"番号: " & Err.Number & vbCrLf & _
"内容: " & Err.Description, vbCritical, "実行エラー"
' エラーが起きても、必ずクリーンアップ処理へ飛ばす
Resume CleanUp
CleanUp:
' 4. 【最重要】オブジェクトの破棄(クリーンアップ)
' エラーが起きても起きなくても、必ずここを通るように設計する
' Recordsetは、CloseしてからNothingにする
If Not rs Is Nothing Then
On Error Resume Next ' 破棄自体でエラーが出ても無視して進む(念のため)
rs.Close
Set rs = Nothing
End If
' Databaseオブジェクトの破棄
If Not db Is Nothing Then
Set db = Nothing
End If
' 最後に、正常終了なのかエラー終了なのかをユーザーに伝える(必要に応じて)
' ここでは、エラーが発生していなければメッセージを出す
If Err.Number = 0 Then
MsgBox "処理が正常に完了しました。", vbInformation
End If
End Sub
4. このコードの「凄さ」を解説します
なぜこの書き方が最強なのか、3つのポイントに分けて解説します。
① `GoTo CleanUp` による「一本道」の設計
メインの処理が成功しても、エラーが起きても、最終的に必ず `CleanUp:` というラベルへ向かうように設計されています。これにより、「片付けを忘れる」というミスを構造的に防いでいるのです。
② `If Not rs Is Nothing Then` の防衛策
もし、`Set rs = db.OpenRecordset(…)` の行自体でエラーが起きた場合、`rs` はまだ空っぽ(Nothing)の状態です。空っぽのものに対して `.Close` を実行しようとすると、別のエラーが発生してしまいます。
`If Not … Is Nothing` と書くことで、「もしオブジェクトがちゃんと存在しているなら、閉じる」という慎重な動きが可能になります。
③ `On Error Resume Next` による二重エラーの防止
クリーンアップ処理の中で万が一エラーが起きても、そこでプログラムが止まっては意味がありません。クリーンアップ内では、一時的にエラーを無視して、最後まで確実にメモリ解放を完遂させるのがプロのテクニックです。
まとめ:ここをクリアすれば、Access VBAの基本はバッチリですよ
オブジェクトの破棄をマスターすることは、単にエラーを防ぐだけではありません。
「自分がコンピュータのリソースをコントロールしている」という感覚を持つことです。
1. オブジェクトは「借り物」であると意識する。
2. エラーが起きても必ず「返却」できるルート(CleanUp)を作る。
3. `Set Nothing` は、プログラムの終わりにおける「儀式」として欠かさない。
この3点を守るだけで、あなたの作るツールは、プロが作ったような「堅牢で、止まらない、信頼できるシステム」へと進化します。
最初は少し記述量が多く感じるかもしれません。でも、一度この「黄金の定型句」を指に覚え込ませてしまえば、もう二度とメモリリークに悩まされることはありませんよ。
さあ、このテンプレートを使って、新しいコードを書き始めてみましょう!応援しています。
