【入門編】DAOのRecordsetを閉じるタイミング:フォームクローズ時とプロシージャ終了時のベストプラクティス – Access VBA解析バイブル

スポンサーリンク
スポンサーリンク

Access VBAの深淵へようこそ:Recordsetを「正しく閉じる」ことでメモリと安定性を支配する

「マクロの記録から一歩踏み出し、VBAを書き始めた。コードは動く。でも、なぜか使い続けるとAccessが重くなるし、時々『書き込み禁止』のエラーが出る……」

もしあなたがそう感じているなら、おめでとうございます。あなたは今、プログラミングの「概念」から「設計(アーキテクチャ)」へと進化する境界線に立っています。

こんにちは。今日は、Access VBAを扱う上で避けては通れない、しかし多くの初学者が「なんとなく」で済ませてしまい、後に大きな痛手(メモリリークやファイル破損)を招くテーマについてお話ししましょう。

今回のテーマは、DAOのRecordset(レコードセット)のライフサイクル管理です。

「開いたら閉じる」。当たり前のことのように聞こえますが、「いつ」「どこで」「どのような手順で」閉じるのか。この一線を理解するだけで、あなたの作るアプリケーションの堅牢性は劇的に向上します。

1. Recordsetとは「本」である

まず、イメージを固めましょう。データベース(テーブル)を「巨大な図書館」だと考えてください。

  • Table(テーブル): 図書館の書棚に並んでいる本。
  • Recordset(レコードセット): あなたが「読みたい」と思って、書棚から取り出し、机の上に広げた本。

Recordsetを操作するということは、本を開いてページをめくっている状態です。
ここで重要なのは、「本を広げたまま放置して、席を立たない」ということです。

もし、あなたが本を広げたまま(Recordsetを開いたまま)プロシージャを終了したり、フォームを閉じたりすると、Accessはその本が「まだ使われている」と判断します。すると、他の人がその本を書き換えようとしたときに「使用中ですよ!」というエラーが出たり、メモリという名の「机のスペース」がどんどん占領されて、Access全体の動作が重くなったりするのです。

2. 二つの「スコープ」と閉じるタイミング

Recordsetには、大きく分けて二つの「寿命(スコープ)」があります。ここを整理することが、マスターへの第一歩です。

① プロシージャ内での使い捨て(ローカルスコープ)

特定のボタンを押したときや、計算を行ったときだけ一時的に使うパターンです。

  • 寿命: そのプロシージャ(`Sub` や `Function`)が終わるまで。
  • 閉じるタイミング: プロシージャの最後(またはエラー発生時)。

② フォームと一緒に生きる(モジュールレベルスコープ)

フォームが開いている間中、ずっとデータを保持しておきたいパターンです。

  • 寿命: フォームが開いている間ずっと。
  • 閉じるタイミング: フォームが閉じるとき(`Form_Close` イベント)。

3. 実践:プロシージャ内でのベストプラクティス

まずは、最も頻繁に使う「プロシージャ内での管理」を見てみましょう。
ここで最も重要なのは、「途中でエラーが起きても、必ず閉じる」という鉄則です。

''' <summary>
''' 特定の条件に一致するデータを集計してメッセージボックスに表示する例
''' </summary>
Public Sub ShowSummaryData()
    Dim db As DAO.Database
    Dim rs As DAO.Recordset
    Dim strSql As String
    
    ' エラーハンドリングの設定(これがプロの必須技術です)
    On Error GoTo ErrorHandler

    Set db = CurrentDb
    strSql = "SELECT ProductName, Price FROM T_Products WHERE Price > 1000"
    
    ' 1. Recordsetを開く(本を机に広げる)
    Set rs = db.OpenRecordset(strSql, dbOpenSnapshot)

    ' データが存在するかチェック
    If Not rs.EOF Then
        Do Until rs.EOF
            Debug.Print rs!ProductName & ": " & rs!Price
            rs.MoveNext
        Loop
    End If

    ' --- ここで正常終了の準備 ---
    
CleanUp:
    ' 2. 【最重要】Recordsetを閉じて、メモリを解放する
    ' エラーが起きても起きなくても、必ずここを通るように設計します
    If Not rs Is Nothing Then
        rs.Close         ' 本を閉じる
        Set rs = Nothing ' 机から本を片付ける(参照を空にする)
    End If
    
    If Not db Is Nothing Then
        Set db = Nothing
    End If
    Exit Sub

ErrorHandler:
    ' エラーが発生した場合の処理
    MsgBox "エラーが発生しました: " & Err.Description, vbCritical
    Resume CleanUp ' エラーが起きてもCleanUp(後始末)へジャンプさせる
End Sub

ここがプロの視点!

  • `rs.Close` と `Set rs = Nothing` の違い:

`Close` は「本を閉じる」行為。`Set rs = Nothing` は「手に持っている本を置いて、手が空の状態にする」行為です。両方セットで行うのが、メモリを完全に解放する作法です。

  • `Resume CleanUp`:

エラーが起きた瞬間にプログラムが止まってしまうと、`Close` が実行されません。エラーハンドラから後始末用のラベル(`CleanUp`)へ飛ばすことで、「どんなトラブルがあっても、必ず後始末をしてから帰る」という安全な設計になります。

4. 実践:フォームレベルでの管理

次に、フォームの変数としてRecordsetを保持する場合です。これは、フォーム上のコンボボックスの値を動的に変えたり、独自のリスト表示を行ったりする場合に使います。

Option Compare Database
Option Explicit

' モジュールレベル変数として宣言(フォームが開いている間、ずっと保持される)
Private m_rs As DAO.Recordset

''' <summary>
''' フォームが開いたときにデータをロードする
''' </summary>
Private Sub Form_Open(Cancel As Integer)
    On Error GoTo ErrorHandler
    
    ' フォームが開く際にRecordsetをセット
    Set m_rs = CurrentDb.OpenRecordset("SELECT * FROM T_Categories", dbOpenSnapshot)
    
    ' 何らかの処理...
    Exit Sub

ErrorHandler:
    MsgBox "ロード中にエラーが発生しました。"
End Sub

''' <summary>
''' フォームが閉じるときに確実に解放する
''' </summary>
Private Sub Form_Close()
    ' フォームが閉じるとき、メモリ上に残ったRecordsetを確実に掃除する
    On Error Resume Next ' 既に閉じている場合などのエラーを無視
    If Not m_rs Is Nothing Then
        m_rs.Close
        Set m_rs = Nothing
    End If
    Debug.Print "Recordsetを安全に解放しました。"
End Sub

なぜ `Form_Close` なのか?

フォームを閉じた後も Recordset がメモリに残っていると、Accessが「まだこのフォームのデータを使っているユーザーがいる」と誤解し、データベースへのロック(排他制御)が解除されない原因になります。`Form_Close` は、そのフォームが消滅する最後の瞬間、確実に後始末をするための聖域です。

5. まとめ:これだけは覚えておこう!

最後に、あなたが明日から現場で使える「黄金律」をまとめます。

項目 プロシージャ内(ローカル) フォーム内(モジュールレベル)
主な用途 一時的な計算、データの書き出し 画面表示、継続的なデータ参照
閉じる場所 プロシージャの最後(`CleanUp`ラベル) `Form_Close` イベント
必須の手順 `rs.Close` $\rightarrow$ `Set rs = Nothing` `rs.Close` $\rightarrow$ `Set rs = Nothing`
最大の敵 エラー発生による「閉じる処理」のスキップ フォームを閉じた後の「解放漏れ」

「開けたら、必ず閉じる。エラーが起きても、必ず閉じる。」

この意識を持つだけで、あなたのAccessアプリケーションは、プロが作ったような「止まらない、重くならない、壊れない」システムへと進化します。

最初は少し面倒に感じるかもしれません。でも、この「後始末の美学」を身につけたとき、あなたはもう、単なるマクロ使いではなく、真のVBAエンジニアへの階段を登りきっているはずですよ。

さあ、次のコードを書くときは、ぜひ `CleanUp` ラベルを意識してみてくださいね。応援しています!

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