Access VBAの「フリーズ」を終わらせる:DAO.RecordsetとSysCmdによる進捗表示の極意
Accessで数万件のレコードを処理するバッチを走らせたとき、画面が白くなり、ユーザーが「フリーズした」と勘違いしてタスクマネージャーから強制終了する……。そんな悲劇を繰り返すのは、もう終わりにしましょう。
プロのエンジニアにとって、「処理中」であることをユーザーに視覚的に伝えるのは、機能の一部ではなく「礼儀」です。
今回は、AccessのDAO.Recordsetが持つ`AbsolutePosition`と、`Application.SysCmd`を組み合わせた、最も堅牢で軽量な進捗バーの実装方法を伝授します。
—
なぜ「ループ内のDoEvents」だけでは不十分なのか
多くの初学者は、ループの中に`DoEvents`を入れるだけで安心します。しかし、それだけでは「いつ終わるのか」「本当に動いているのか」というユーザーの不安は解消されません。
また、頻繁に画面を書き換える処理はパフォーマンスを著しく低下させます。我々が求めるのは、「計算コストを最小限に抑えつつ、OSに処理権を返し、進捗を正確に反映させる」という極めて高度なバランスです。
—
堅牢な設計:DAO.Recordsetの特性を活かす
DAOの`Recordset`には`RecordCount`プロパティがありますが、これには罠があります。`dbOpenDynaset`などで開いた直後は、全レコードを走査していないと`RecordCount`が正確な値を返さないケースがあるのです。
したがって、以下の手順で設計するのが「プロの定石」です。
1. MoveLastとMoveFirst: まずレコードセットの末尾へ移動し、正確な全件数を確定させる。
2. AbsolutePositionの活用: 0から始まるインデックスを利用して、進捗率をリアルタイムに計算する。
3. SysCmdの活用: Access標準のステータスバーを操作し、余計なフォーム(進捗バー用フォーム)を作らずに軽量に実装する。
—
実装コード:そのまま現場で使える「プロ仕様」のテンプレート
以下のコードは、保守性を考慮し、進捗更新処理を独立させた構成にしています。
Public Sub ProcessLargeData()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim totalRecords As Long
Dim currentPos As Long
Set db = CurrentDb
Set rs = db.OpenRecordset(“SELECT FROM T_TargetTable”, dbOpenDynaset)
‘ 1. レコードセットの全件数を確定させる
If Not rs.EOF Then
rs.MoveLast
totalRecords = rs.RecordCount
rs.MoveFirst
Else
Exit Sub
End If
‘ 2. SysCmdで進捗バーを初期化
Call SysCmd(acSysCmdInitMeter, “データ処理中…”, totalRecords)
On Error GoTo Cleanup
‘ 3. ループ処理
Do Until rs.EOF
‘ — ここに実際の業務ロジックを記述 —
‘ 例: rs.Edit: rs!Status = “Processed”: rs.Update
‘ ———————————-
‘ 4. 進捗状況を更新 (AbsolutePositionは0ベースのため +1 する)
currentPos = rs.AbsolutePosition + 1
‘ 毎件更新すると重いため、100件ごと等の間引きを入れるのがパフォーマンスの秘訣
If currentPos Mod 100 = 0 Or currentPos = totalRecords Then
Call SysCmd(acSysCmdUpdateMeter, currentPos)
DoEvents ‘ UIの応答性を確保
End If
rs.MoveNext
Loop
Cleanup:
‘ 5. 必ず終了処理を行う(これを忘れるとステータスバーが消えない)
Call SysCmd(acSysCmdRemoveMeter)
If Not rs Is Nothing Then rs.Close: Set rs = Nothing
Set db = Nothing
MsgBox “処理が完了しました。”, vbInformation
End Sub
—
この実装のアーキテクト的ポイント
1. 「間引き」による高速化
`currentPos Mod 100 = 0` の条件を入れている点が重要です。1レコードごとに`SysCmd`を呼び出すのはオーバーヘッドが大きく、処理速度を大幅に低下させます。数万件の処理なら100〜500件ごとの更新で十分、視認性と速度の両立が可能です。
2. エラーハンドリングと終了処理
`SysCmd(acSysCmdRemoveMeter)`は必ず`Cleanup`ラベルで実行するようにしてください。エラーが発生した際に進捗バーがステータスバーに残ってしまうのは、ツールとして非常に格好が悪く、ユーザーに無用な混乱を与えます。
3. DAOのライフサイクル管理
`Recordset`を閉じる際、明示的に`Nothing`を代入するのはVBAの基本ですが、これはメモリリークを防ぐための最低限の作法です。大規模バッチ処理では、この積み重ねがAccessの安定性に直結します。
—
最後に:エンジニアとしてのマインドセット
「動けばいい」というコードは、数ヶ月後の自分を苦しめます。
今回紹介した手法は、Accessという枯れた技術の中でも、「ユーザー体験(UX)を意識したプロフェッショナルな設計」の第一歩です。
ステータスバーという限られたリソースを使いこなし、ユーザーが安心して使えるツールを提供してください。それが、あなたの開発者としての価値を一段引き上げることになります。
何か疑問や、より深い最適化の相談があればいつでもどうぞ。あなたのコードがより堅牢になることを期待しています。
