【入門編】ADOの接続プールを強制的に解放する:長時間稼働するAccessアプリのメモリ管理術 – Access VBA解析バイブル

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

Accessアプリが重くなる原因を断つ。ADO接続プールの制御と、メモリを食いつぶさないための『作法』

「朝、使い始めた時はサクサク動いていたのに、午後になると動作がもっさりしてくる……」
「長時間アプリを開きっぱなしにしていると、たまにエラーが出て強制終了してしまう」

Access VBAで業務アプリケーションを開発していると、一度はこうした「謎のパフォーマンス低下」に直面するはずです。もしあなたが、マクロの記録から一歩踏み出し、「もっと安定した、プロ仕様のツールを作りたい」と考えているなら、今日お話しする内容は、あなたのエンジニア人生を変える重要なターニングポイントになるでしょう。

今日は、Accessが外部データベース(SQL Serverなど)と通信する際に使う「ADO(ActiveX Data Objects)」の裏側に潜む、メモリ管理の真実についてお話しします。

1. なぜ「接続」はメモリを食いつぶすのか?

まず、基礎知識として「DAO」と「ADO」の違いを整理しておきましょう。ここを混同していると、トラブルの際に対処ができません。

  • DAO (Data Access Objects): Accessの「内部」に特化した職人です。Access内のローカルテーブルを操作するなら、これが最速です。
  • ADO (ActiveX Data Objects): 外部の世界(SQL ServerやOracleなど)と通信するための「外交官」です。ネットワーク越しにデータを取ってくるなら、ADOの出番です。

多くの業務アプリでは、データの安全性と速度のために、データ本体はSQL Serverに置き、Accessは「窓口」としてADOを使って接続します。

犯人は「接続プール」という名の親切心

ここで問題になるのが、「接続プール(Connection Pooling)」という仕組みです。

データベースとの接続(コネクション)を確立するのは、実は非常にコスト(時間とメモリ)がかかる作業です。そのため、Windows(ADO)は「次にまた使うかもしれないから、一度切断した接続をすぐに捨てずに、メモリの中に『スタンバイ状態』で保持しておこう」という親切な機能を持っています。これが接続プールです。

しかし、この親切心が、長時間稼働するAccessアプリにおいては「メモリを占有し続ける原因」へと豹変します。接続を閉じたつもりでも、裏側では接続が「プール」としてメモリに残り続け、それが積み重なることでアプリが重くなっていくのです。

2. プロの作法:オブジェクトのライフサイクルを支配する

初学者が陥りがちな罠は、変数に接続を代入したまま、その「後始末」を忘れてしまうことです。

避けるべき「悪い例」

' モジュールレベル(宣言部)で宣言してしまう
Dim conn As ADODB.Connection 

Sub BadDataFetch()
    Set conn = New ADODB.Connection
    conn.Open "接続文字列..."
    ' ... 処理 ...
    ' ここで Close も Set Nothing もしていない!
    ' 接続はメモリの中に「幽霊」のように残り続けます。
End Sub

このように、変数のスコープ(有効範囲)が広すぎたり、終了処理が抜けていたりすると、メモリ管理が効かなくなります。

理想的な「黄金のパターン」

プロのエンジニアは、「使い終わったら、即座に、確実に、跡形もなく消す」という原則を徹底します。

1. `Close` で、データベースとの通信を終了させる。
2. `Set … = Nothing` で、VBA上のオブジェクト変数をメモリから解放する。
3. `Error Handling` を使い、エラーが起きても必ず上記の手順を通るようにする。

3. 実践:メモリを解放する「堅牢な接続コード」

それでは、実際に現場で使える、メモリ管理を意識したコードを見てみましょう。
ポイントは、「エラーが起きても、必ず後片付け(Cleanup)へジャンプさせる」という構造です。

Option Explicit

''' <summary>
''' 外部データベースからデータを安全に取得するサンプル
''' </summary>
Public Sub SafeDataFetch()
    ' 1. 変数の宣言(スコープはできるだけ狭く!)
    Dim conn As ADODB.Connection
    Dim rs As ADODB.Recordset
    Dim strConn As String
    Dim strSQL As String

    ' 接続文字列(実際には環境に合わせて設定してください)
    strConn = "Provider=SQLOLEDB;Data Source=YourServer;Initial Catalog=YourDB;Integrated Security=SSPI;"
    strSQL = "SELECT TOP 10 * FROM SalesData"

    ' エラーハンドリングの設定
    On Error GoTo ErrorHandler

    ' 2. オブジェクトの生成と接続
    Set conn = New ADODB.Connection
    conn.Open strConn
    
    Set rs = New ADODB.Recordset
    ' カーソルタイプを適切に設定(メモリ節約のため、必要以上に大きくしない)
    rs.Open strSQL, conn, adOpenForwardOnly, adLockReadOnly

    ' 3. データの処理
    If Not rs.EOF Then
        Debug.Print "データ取得成功: " & rs.Fields(0).Value
    End If

' --- ここからが「プロ」の仕事 ---
Cleanup:
    ' 4. 後始末(Cleanupセクション)
    ' エラーが起きても、正常終了しても、必ずここを通るように設計します
    
    ' Recordsetの解放
    If Not rs Is Nothing Then
        If rs.State = adStateOpen Then rs.Close
        Set rs = Nothing
    End If
    
    ' Connectionの解放
    If Not conn Is Nothing Then
        ' 接続を閉じてから、オブジェクトをNothingにする
        If conn.State = adStateOpen Then conn.Close
        Set conn = Nothing
    End If
    
    ' ログ出力(デバッグ用)
    Debug.Print "リソースの解放が完了しました。"
    Exit Sub

ErrorHandler:
    ' エラーが発生した場合は、エラー内容を表示してCleanupへ飛ばす
    MsgBox "エラーが発生しました: " & Err.Description, vbCritical, "実行エラー"
    Resume Cleanup
End Sub

コードの解説:なぜこれでメモリが守られるのか?

  • `adOpenForwardOnly` / `adLockReadOnly`: Recordsetを開く際、これらを指定することで「読み取り専用・前方移動のみ」という制限をかけます。これにより、ADOが保持するメモリ消費量を最小限に抑えられます。
  • `Resume Cleanup`: これが最大の肝です。エラーが発生して処理が中断しても、いきなりプログラムを終了させるのではなく、`Cleanup`ラベルへ強制的にジャンプさせます。これにより、「エラーが起きたから接続が開いたまま放置される」という最悪の事態を防いでいるのです。
  • `If Not … Is Nothing`: オブジェクトがすでに存在しているか(メモリに載っているか)を確認してから操作しています。これは、二重に閉じようとしてエラーが出るのを防ぐための、極めて丁寧な作法です。

4. さらに上を目指すあなたへ:接続プールの「リセット」

もし、上記のような丁寧なコードを書いていても、どうしてもメモリ使用量が減らない、あるいは接続が「固まった」ような挙動を見せる場合は、物理的に接続をリセットする必要があります。

通常、VBAから接続プールそのものを直接操作するコマンドはありません。しかし、「接続文字列に特定のオプションを加える」ことで、接続の挙動を制御できる場合があります。

例えば、接続文字列の末尾に `;Pooling=False` と記述すると、接続プール機能そのものを無効化できます。

  • メリット: 接続を閉じれば、メモリから即座に解放される。長時間稼働アプリには非常に安全。
  • デメリット: 毎回ゼロから接続を確立するため、データの取得スピードがわずかに落ちる。

「速度」と「安定性」のトレードオフです。もし、あなたのアプリが「1分間に何度も接続と切断を繰り返す」タイプなら、Poolingは `True`(デフォルト)にすべきです。しかし、「数分〜数十分おきに一度だけ接続する」ようなタイプなら、`Pooling=False` に設定するのが、最も賢明なメモリ管理術となります。

まとめ:ここをクリアすれば、Access VBAの基本はバッチリですよ

いかがでしたか?

1. ADOは「外交官」であり、接続プールという「親切な仕組み」がメモリを消費する。
2. 「Openしたら必ずClose、そしてNothing」というライフサイクルを徹底する。
3. エラーハンドリング(`Resume Cleanup`)を使って、どんな時も後片付けを行う。
4. 必要に応じて、接続文字列でプーリングの挙動をコントロールする。

これらを意識するだけで、あなたの作るアプリケーションの品質は、単なる「マクロの集まり」から、「プロフェッショナルな業務システム」へと昇華します。

一歩ずつ、確実に。コードの裏側にある「動き」を想像しながら、素晴らしい開発ライフを送ってくださいね。応援しています!

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