【Access VBA極限の知見】DAO.RecordsetのAbsolutePositionで実現する、UXを極めた進捗バー実装術
開発現場でよく見かける光景がある。数万件のレコードを処理する巨大なバッチ処理が走った瞬間、画面がフリーズしたようになり、ユーザーは「固まったのか?」「強制終了すべきか?」と怯えながら祈るように待つ。
プロとして、これは悪だ。
どれほど処理ロジックが美しくとも、そこに「対話性(インタラクティブ性)」が欠けていれば、アプリケーションの評価は地に落ちる。ユーザーの不安を解消し、システムの信頼性を担保するために不可欠なのが「進捗の可視化」である。
今回は、Access VBAの王道でありながら、正しく理解されていないと致命的なパフォーマンス劣化を招く `DAO.Recordset.AbsolutePosition` を使った、極限まで洗練された進捗バーの動定更新テクニックを伝授する。
—
1. なぜ `RecordCount` だけでは実務で使い物にならないのか?
多くの初学者は、進捗率を計算する際にこう書く。
‘ 【アンチパターン】これをしてはいけない
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset(“T_HeavyData”, dbOpenSnapshot)
rs.MoveLast ‘ ここが地獄の始まり
Dim total As Long
total = rs.RecordCount
rs.MoveFirst
Do Until rs.EOF
‘ 処理…
rs.MoveNext
Loop
現場を崩壊させる2つの致命的罠
1. `MoveLast` のオーバーヘッド: `dbOpenSnapshot` や `dbOpenDynaset` において、全レコードをローカルキャッシュに引き込むまで `RecordCount` は正確な値を返さない。数百万件のテーブルでこれをやると、進捗バーを表示する前の段階で数秒〜数十秒の硬直が発生する。
2. 動的な増減への無力: 処理の途中で別プロセスがレコードを追加・削除した場合、最初の `RecordCount` は完全に意味を失う。
ここで登場するのが、今回主役の `AbsolutePosition` である。
—
2. `AbsolutePosition` の正体と、真のパフォーマンス特性
`AbsolutePosition` プロパティは、カレントレコードのゼロベースのインデックス(何番目にいるか)を返す。
- `rs.AbsolutePosition` は `0` から始まる。
- 処理の現在地を `0` から `RecordCount – 1`(厳密には `RecordCount` が確定していれば)の範囲で即座に取得できる。
しかし、ここで一つ重要なアーキテクチャ上の注意がある。
「`AbsolutePosition` を参照した瞬間、DAOはレコードの総数を数えに行くことがある」 という特性だ。特にダイナセットやスナップショットの初期段階では、`AbsolutePosition` を評価するタイミングによってパフォーマンス挙動が変わる。
これを完璧にコントロールし、かつUIの描画負荷(画面のちらつきやフリーズ)を抑え込むための「プロダクションコード」を次章で公開する。
—
3. 【コピペOK】実務で即採用できる堅牢な進捗バー制御コード
以下のコードは、ステータスバー(Accessウィンドウの最下部)およびビジュアルなプログレス表示を組み合わせた、実務仕様のモジュールである。エラーハンドリング、トランザクション、そしてUI更新頻度の間引き(スロットリング)まで考慮されている。
Option Compare Database
Option Explicit
‘ ==============================================================================
‘ 処理名 : ExecuteHeavyBatchWithProgress
‘ 概要 : 大量データ処理に対し、AbsolutePositionを用いて進捗をステータスバーに表示する
‘ ==============================================================================
Public Sub ExecuteHeavyBatchWithProgress()
On Error GoTo ErrorHandler
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim strSQL As String
Dim lngTotal As Long
Dim lngCurrent As Long
Dim dblPercent As Double
Dim lngStartTime As Double
lngStartTime = Timer
Set db = CurrentDb
‘ 1. 対象データの取得(パフォーマンス最優先のためスナップショットを使用)
strSQL = “SELECT ID, ProcessingTargetField FROM T_MassData WHERE IsProcessed = False;”
Set rs = db.OpenRecordset(strSQL, dbOpenSnapshot)
‘ 2. レコードが存在しない場合のガード
If rs.EOF Then
MsgBox “処理対象のデータは存在しません。”, vbInformation, “完了”
GoTo Cleanup
End If
‘ 3. レコード数の確定(MoveLastのコストを最小限にするイディオム)
rs.MoveLast
lngTotal = rs.RecordCount
rs.MoveFirst
‘ ステータスバーの初期化
SysCmd acSysCmdInitMeter, “一括処理を実行中…”, lngTotal
‘ 4. メインループ
Do Until rs.EOF
‘ ———————————————————————-
‘ ここに実際の業務ロジックを記述
‘ 例: Call ProcessEachRecord(rs!ID)
‘ ———————————————————————-
‘ 現在位置の取得(0基底なので +1 する)
lngCurrent = rs.AbsolutePosition + 1
‘ UI更新の最適化:
‘ すべてのレコードでSysCmdを呼ぶと描画フックがボトルネックになるため、
‘ 100件に1回、または最後のレコードの時だけステータスバーを更新する
If lngCurrent Mod 100 = 0 Or lngCurrent = lngTotal Then
SysCmd acSysCmdUpdateMeter, lngCurrent
‘ ユーザーのキャンセル操作や画面描画を担保するためにDoEventsを挟む
DoEvents
End If
rs.MoveNext
Loop
‘ ステータスバーのクリア
SysCmd acSysCmdRemoveMeter
MsgBox “処理が正常に完了しました。” & vbCrLf & _
“処理件数: ” & lngTotal & ” 件” & vbCrLf & _
“所要時間: ” & Format(Timer – lngStartTime, “0.00”) & ” 秒”, _
vbInformation, “処理終了”
Cleanup:
On Error Resume Next
If Not rs Is Nothing Then rs.Close: Set rs = Nothing
Set db = Nothing
Exit Sub
ErrorHandler:
‘ 異常終了時のステータスバー残留を防ぐ
SysCmd acSysCmdRemoveMeter
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“Error ” & Err.Number & “: ” & Err.Description, _
vbCritical, “システムエラー”
Resume Cleanup
End Sub
—
4. チーフアーキテクトが教える、現場で活きる実装の急所
上記のコードには、単なる「動くコード」を超えたプロの知見が凝縮されている。
① `SysCmd` API の活用
Access標準の `SysCmd acSysCmdInitMeter` を使えば、専用のフォームを作らなくても、Accessウィンドウのステータスバーに美しいプログレスバーを表示できる。余計なフォームオブジェクトのロード・アンロードが発生しないため、メモリ効率の面でも優れている。
② 更新頻度の「間引き(Throttling)」
VBAの `DoEvents` や画面描画(UIスレッドへの割り込み)は、実は非常に重い処理だ。もし1件処理するごとに `SysCmd acSysCmdUpdateMeter` を呼ぶと、純粋なデータ処理速度が最大で10分の1以下に落ちる。
`lngCurrent Mod 100 = 0` のように、「数件に1回」の頻度で更新を間引くこと。これが、爆速バッチ処理とUXを両立させるための鉄則である。
③ エラーハンドリングとクリーンアップの確実性
バッチ処理中にエラーやユーザーによる強制中断(Ctrl + Break)が発生した場合、ステータスバーにメーターが残ったままになり、最悪の場合Access全体が不安定になる。
必ず `ErrorHandler` 内で `SysCmd acSysCmdRemoveMeter` を呼び出し、UIの状態をクリーンに復元させなければならない。
—
最後に:プロフェッショナルなツールとは
業務自動化ツールを作るということは、単に「手作業をプログラムに置き換える」ことではない。「使う人間のストレスとリスクを極限までゼロにする」ことだ。
`AbsolutePosition` を正しく手懐け、堅牢なエラーハンドリングとスムーズな進捗表示を実装したアプリケーションは、それだけで「信頼されるシステム」へと昇華する。
ぜひ、あなたの現場の巨大バッチ処理にこの知見を組み込み、ユーザーを「待つ苦痛」から解放してやってほしい。
