【入門編】Application.CurrentDbをキャッシュすべき理由:DAO.Database変数の再利用によるパフォーマンス最適化 – Access VBA解析バイブル

スポンサーリンク

こんにちは!Access VBAの世界へようこそ。
マクロの記録から一歩踏み出し、「自分の手でシステムをコントロールしたい!」と熱意を燃やしているあなたへ、今日はエンジニアとして絶対に知っておいてほしい『Access VBAの裏側の仕組みと、劇的な高速化テクニック』をお伝えします。

ここをクリアすれば、あなたの書くコードは「動くだけの初心者コード」から「プロのアーキテクトが書く洗練されたコード」へと劇的に生まれ変わります。心して聞いてくださいね。

1. なぜ、あなたのループ処理は遅いのか?

Accessで大量のデータを処理するとき、こんなコードを書いたことはありませんか?

‘ 【アンチパターン】ループのたびに CurrentDb を呼ぶ愚行
Dim i As Long
For i = 1 to 10000
‘ ここで毎回データベースのオブジェクトが新規生成されている!
CurrentDb.Execute “UPDATE T_Log SET Status = ‘Processed’ WHERE ID = ” & i, dbFailOnError
Next i

「1万件のデータを更新するのに、何秒も待たされる……Accessって遅いな」
そう思っていませんでしたか?

実は、Accessが遅いのではありません。あなたの書き方が、Accessのエンジンに無駄な労働を強いていたのです。
この現象の裏側を、分かりやすく解き明かしていきましょう。

2. `CurrentDb` の正体と、恐ろしい「オブジェクト生成のコスト」

私たちは普段、何気なく `CurrentDb` という魔法の言葉を使って、現在開いているデータベースにアクセスしています。

Dim db As DAO.Database
Set db = CurrentDb

初心者の方の多くは、「`CurrentDb` は、今開いているデータベースを指し示す『変数』のようなもの」と誤解しています。しかし、ここが決定的な勘違いのポイントです。

> 【極限の知見】`CurrentDb` は変数ではなく、「関数(メソッド)」である

`CurrentDb` を呼び出すたびに、Accessの内部(C++で書かれた底辺のエンジン)では、以下のような重たい処理が裏で行われています。

1. 現在のデータベースへの接続セッションを確認する
2. 新しく `DAO.Database` オブジェクトをメモリ上に「新規生成(インスタンス化)」する
3. そのオブジェクトをVBA側に返す

つまり、先ほどの1万回ループのコードでは、1万回も新しいデータベース接続オブジェクトをメモリ上で作り、捨てて、を繰り返していたのですそりゃあパソコンも疲れてしまいますよね。

3. 解決策:`CurrentDb` をキャッシュせよ!

この無駄を排除するのは拍子抜けするほど簡単です。
「最初に1回だけ `CurrentDb` を変数(キャッシュ)に代入し、ループ内ではその変数を使い回す」。これだけです。

黄金の設計パターン(モジュールレベルでの保持)

Sub FastUpdateData()
Dim db As DAO.Database
Dim i As Long

‘ 【重要】ループに入る前に「1回だけ」CurrentDbを呼んで変数に格納する(キャッシュ)
Set db = CurrentDb

‘ データベースのトランザクションを開始すると、さらに爆速になります
db.BeginTrans
On Error GoTo Rollback_Process

For i = 1 To 10000
‘ 生成済みの db 変数を使い回すので、オーバーヘッドがゼロに!
db.Execute “UPDATE T_Log SET Status = ‘Processed’ WHERE ID = ” & i, dbFailOnError
Next i

‘ コミットして確定
db.CommitTrans
MsgBox “処理が完了しました!”, vbInformation
Exit Sub

Rollback_Process:
‘ エラーが起きたら変更を巻き戻す
db.Rollback
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical
End Sub

このコードが圧倒的に速い理由

1. オブジェクト生成の排除: `CurrentDb` の呼び出しが1回だけになるため、メモリの消費とCPUの無駄な処理が激減します。
2. トランザクションの併用: `BeginTrans` と `CommitTrans` を組み合わせることで、ハードディスクへの書き込み回数をまとめ、物理的な処理速度も跳ね上がります。

4. 陥りやすい罠:オブジェクト変数の解放(解放の美学)

プログラミングの世界には、「確保したメモリは責任を持って片付ける」という美学があります。
先ほど作成した `DAO.Database` 変数ですが、プロシージャ(Sub)の終わりでどうなっているでしょうか?

実は、VBAではプロシージャが終了するときに、ローカル変数(Subの中で宣言した変数)は自動的にメモリから解放されます。そのため、上記のコードでも実害はありません。

しかし、もしこれが「フォーム全体で使い回すモジュールレベル変数(`Private db As DAO.Database`)」だった場合は注意が必要です。
オブジェクトを開いたまま放置すると、メモリリーク(メモリの無駄遣い)を引き起こし、Access全体の動作が不安定になる原因を作ります。

‘ モジュールレベルで宣言する場合の作法
Private m_db As DAO.Database

Public Function GetDatabase() As DAO.Database
‘ まだ開いていない、または参照が切れていたら生成する(遅延初期化パターン)
If m_db Is Nothing Then
Set m_db = CurrentDb
End If
Set GetDatabase = m_db
End Function

このように「必要になったら作り、アプリが閉じるときに綺麗に片付ける」という意識を持つことが、プロのエンジニアへの第一歩です。

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

今回のまとめです。

  • `CurrentDb` は変数ではなく関数。呼ぶたびに重たいオブジェクトが新規作成される。
  • 大量のレコードを扱うループ処理の前で `CurrentDb` を変数に代入(キャッシュ)せよ。
  • 使い回すことで、Accessのパフォーマンスは劇的に向上する。

「動けばいいや」のコードから卒業し、裏側の仕組みを意識したエレガントなコードが書けるようになると、Access VBAを扱うのが何倍も楽しくなりますよ。

あなたの開発ライフが、より快適で知的で満ちたものになりますように。
それでは、次の現場でお会いしましょう!

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