【実務・中級編】DAO.RecordsetのAbsolutePositionを活用した、進捗バーの動的更新とユーザー体験の向上 – Access VBA解析バイブル

スポンサーリンク

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)を意識したプロフェッショナルな設計」の第一歩です。

ステータスバーという限られたリソースを使いこなし、ユーザーが安心して使えるツールを提供してください。それが、あなたの開発者としての価値を一段引き上げることになります。

何か疑問や、より深い最適化の相談があればいつでもどうぞ。あなたのコードがより堅牢になることを期待しています。

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