【入門編】DAO.Recordsetの「BatchUpdate」を模倣する:トランザクション管理による一括更新の高速化 – Access VBA解析バイブル

スポンサーリンク

こんにちは!現場でバリバリとAccessやVBAを使っていると、避けて通れないのが「大量データの処理速度問題」ですよね。

「1万件のデータを更新するのに、画面がフリーズしたかのように何分も固まる…」
「コーヒーを飲みに行っても、まだ終わっていない…」

そんな絶望を味わったことはありませんか?
今回は、マクロの記録や単純なループ処理から一歩抜け出して、「Access VBAで爆速のデータ処理を実現する極限の知見」をあなたにお伝えします。

ここをクリアすれば、Access VBAの基本はもちろん、データベースを扱う者としての本質がグッと見えてきますよ。温かく導きますので、一緒にマスターしていきましょう!

1. なぜ大量データの更新は遅いのか?(根本原因の解明)

まず、敵を知ることから始めましょう。なぜループで大量のレコードを更新すると遅いのでしょうか?

初心者がやりがちなコードは、だいたいこんな形をしています。

‘ 【やってはいけない遅い処理の例】
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset(“T_売上明細”, dbOpenDynaset)

Do Until rs.EOF
rs.Edit
rs(“ステータス”).Value = “処理済み”
rs.Update ‘ ←ここで毎回ディスクに書き込みに行っている!
rs.MoveNext
Loop
rs.Close

何が問題か分かりますか?
Access(正確には背後で動くデータベースエンジン「JET / ACE」)は、`rs.Update` が実行されるたびに、「よし、今の変更をディスク(ハードディスクやSSD)に書き込むぞ!」と大立ち回りをします。

1万件あれば、1万回もディスク書き込みの命令(I/O)が発生します。そりゃあ遅いはずです。例えるなら、「1冊の本をコピーするのに、1文字書くごとに印刷所へ走っている」ようなものです。

2. 解決の切り札:トランザクション + トランザクション処理の思想

ここで登場するのが、今回のテーマである「トランザクション管理」です。

トランザクションとは、一言で言うと「一連の処理をひとまとめにして、すべて成功したらまとめて保存(コミット)、失敗したら全部なかったことにする(ロールバック)」という仕組みです。

データベースのエンジニアリングにおいて、トランザクションには「処理の安全性を担保する」という本来の役割のほかに、「ディスクへの書き込みを最後に1回にまとめることで、爆発的なスピードアップを果たす」という強力な副次効果(バッチ処理的な挙動)があります。

DAOにはネイティブな「BatchUpdate」はないけれど…

実は、ADOという別の技術には `BatchUpdate` という一括更新メソッドがあるのですが、Accessの標準である DAO にはそれがありません。
しかし、「DAOのレコードセット操作」と「`DBEngine.BeginTrans`(トランザクション)」を組み合わせることで、完全に同じ、いやそれ以上の高速な一括更新を模倣することができるのです。

3. 実践!爆速一括更新(BatchUpdate模倣)のコード例

それでは、実際の現場でそのまま使える、洗練されたコードを見てみましょう。
今回は「T_売上明細」テーブルの「未処理」となっているステータスを、「処理済み」に一気に書き換えるシナリオです。

Sub FastBatchUpdateSample()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim startTime As Double

‘ 処理時間を計測するためのタイマー開始
startTime = Timer

‘ CurrentDbを変数に収める(※これ重要!毎回CurrentDbを呼ぶと遅くなります)
Set db = CurrentDb

‘ 対象のレコードセットを開く(パフォーマンス重視で dynaset を使用)
Set rs = db.OpenRecordset(“T_売上明細”, dbOpenDynaset)

‘ 【極限の知見①】トランザクションの開始
‘ この宣言により、データベースエンジンは「とりあえずメモリ上で変更を溜め込め」と理解します。
DBEngine.BeginTrans

On Error GoTo ErrorHandler ‘ 万が一のエラーに備える

Do Until rs.EOF
‘ 条件に合うものだけを処理(例として「未処理」のもの)
If rs(“ステータス”).Value = “未処理” Then
rs.Edit
rs(“ステータス”).Value = “処理済み”
rs.Update ‘ この時点ではメモリ上の仮書き込みなので爆速です
End If
rs.MoveNext
Loop

‘ 【極限の知見②】トランザクションの確定(コミット)
‘ ここで初めて、溜め込んだ変更がディスクへ一気に書き込まれます。
DBEngine.CommitTrans

‘ クリーニング
rs.Close
Set rs = Nothing
Set db = Nothing

‘ 完了メッセージ
MsgBox “一括更新が完了しました!処理時間: ” & Format(Timer – startTime, “0.00”) & “秒”, vbInformation
Exit Sub

ErrorHandler:
‘ 【極限の知見③】エラー時のロールバック
‘ 途中でエラーが起きたら、トランザクションを巻き戻してデータを汚さない
DBEngine.Rollback

If Not rs Is Nothing Then rs.Close
Set rs = Nothing
Set db = Nothing

MsgBox “エラーが発生したため、処理を中断し変更をロールバックしました。” & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical
End Sub

4. チーフアーキテクトが教える、コードの急所と極意

上記のコードには、プログラミング初学者が「おっ」と驚くような、しかし実務では常識とされるテクニックが詰まっています。ポイントを3つに分けて解説します。

① `CurrentDb` は変数にキャッシュせよ

VBAで `CurrentDb.OpenRecordset` と書くたびに、Accessは内部で新しいデータベースオブジェクトを生成し、メモリ上にロードし直しています。
これをループや処理のたびにやると大変なオーバーヘッド(無駄な負荷)になります。
`Set db = CurrentDb` と最初に1回だけ変数に受け、以後は `db.OpenRecordset` と呼び出すのがプロの作法です。

② エラーハンドリングと `Rollback` はセット

トランザクションを使うときの鉄則は、「BeginTrans を使ったら、必ず CommitTrans か Rollback のどちらかを通るようにする」ことです。
もしエラー処理(`On Error GoTo`)をサボって途中でコードが止まると、データベースが「トランザクション中」のままロックされてしまい、最悪の場合ファイルが破損します。上のコードのように、必ず `ErrorHandler` ラベルを用意して `Rollback` を仕込みましょう。

③ なぜこれで早くなるのか?

レコードの編集(`Edit` 〜 `Update`)自体はループ内で回っていますが、ディスクへの物理的なI/O(書き込み処理)が `CommitTrans` の瞬間に1回(正確には内部の最適化された単位)に凝縮されるため、体感速度が数倍〜数十倍に跳ね上がります。

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

今回は、DAO.Recordsetとトランザクションを組み合わせた「BatchUpdateの模倣」について解説しました。

  • なぜ遅いのか? → 毎回ディスクに書きに行っているから。
  • どう解決するのか? → `DBEngine.BeginTrans` と `CommitTrans` で書き込みをまとめる。
  • 実務での注意点? → エラー時の `Rollback` を絶対に忘れない。

この「背後でデータベースがどう動いているか(ライフサイクルとI/O)」を意識できるようになると、あなたはもう「初学者」の枠を完全に脱却し、立派な「システムエンジニア」の思考を手に入れています。

ぜひ、あなたの開発するAccessアプリのボトルネック箇所で試してみてください。その圧倒的なスピードの差に驚くはずです。それでは、また次回の極限の知見でお会いしましょう!

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