Accessの「沈黙」を殺せ:DAO.RecordsetとSysCmdによる進捗管理の極意
Accessで数万レコードを超えるバッチ処理を走らせたとき、ユーザーは決まってこう言う。「フリーズした」と。
画面が白濁し、Windowsのタスクマネージャーが「応答なし」と判定するまで、ユーザーの忍耐力は持たない。
我々エンジニアにとって、バッチ処理における「進捗表示」は単なる親切心ではない。「システムが生きていることを証明する生存確認」であり、UX(ユーザー体験)の根幹をなす品質管理そのものだ。
今回は、`DAO.Recordset`の`AbsolutePosition`を最大限に活用し、システムに負荷をかけずに正確な進捗をフィードバックする極限のテクニックを伝授する。
—
1. なぜ「DoEvents」だけで満足してはいけないのか
多くの初学者は、ループ内で`DoEvents`を連打するだけで満足する。しかし、これは単にOSのメッセージキューを捌いているだけであり、ユーザーには「今、何%終わったのか」が全く伝わらない。
我々が目指すのは、`Application.SysCmd`を用いたステータスバーの完全制御だ。だが、ここにも罠がある。SysCmdの更新頻度が過剰であれば、それはCPUの無駄遣いとなり、逆に処理速度を低下させる。
パフォーマンスの最適化ポイント
- 絶対位置の取得コスト: `Recordset.AbsolutePosition`は、カーソルタイプが`dbOpenDynaset`や`dbOpenSnapshot`であれば高速だが、`dbOpenTable`ではインデックスの恩恵を受けにくい。
- 更新頻度の制御: 1レコードごとにSysCmdを叩くのは非効率の極みだ。処理件数に応じて「1%単位」あるいは「一定件数ごと」に描画を制御せよ。
—
2. 実装の極致:進捗管理クラスの設計
コードの保守性を考えれば、進捗管理ロジックをメインのビジネスロジックと分離すべきだ。以下に、現場で即戦力となる実装例を提示する。
‘ —————————————————————————
‘ @Description: 進捗管理用モジュール
‘ @Note: DAO.RecordsetとSysCmdを統合し、オーバーヘッドを最小化する
‘ —————————————————————————
Public Sub ExecuteBatchProcess(strSQL As String)
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim totalRecs As Long
Dim currentRec As Long
Dim stepSize As Long
Set db = CurrentDb
Set rs = db.OpenRecordset(strSQL, dbOpenDynaset)
If rs.EOF Then GoTo Cleanup
‘ レコード数を確定(LastModifiedやRecordCountの特性を理解しておくこと)
rs.MoveLast
totalRecs = rs.RecordCount
rs.MoveFirst
‘ ステータスバーの初期化
SysCmd acSysCmdInitMeter, “バッチ処理実行中…”, totalRecs
‘ 更新頻度のチューニング(100段階、あるいは最大1000回程度の更新に抑える)
stepSize = IIf(totalRecs \ 100 = 0, 1, totalRecs \ 100)
Do Until rs.EOF
‘ — ここにビジネスロジック —
‘ ProcessData(rs)
‘ —————————-
currentRec = rs.AbsolutePosition + 1
‘ 一定件数ごとに進捗を更新(描画負荷を軽減)
If currentRec Mod stepSize = 0 Then
SysCmd acSysCmdUpdateMeter, currentRec
DoEvents ‘ UIスレッドの応答性を確保
End If
rs.MoveNext
Loop
Cleanup:
‘ 終了処理:SysCmdの明示的な解放
SysCmd acSysCmdRemoveMeter
‘ オブジェクトの明示的解放(メモリリークは許されない)
If Not rs Is Nothing Then rs.Close: Set rs = Nothing
Set db = Nothing
End Sub
—
3. シニアエンジニアが意識すべき「見えないコスト」
メモリとリソース管理
`DAO.Recordset`は、`Close`メソッドを呼ぶまでメモリを占有する。特に巨大なバッチ処理において、エラーハンドラで`Resume`する際、`rs`の解放漏れが累積すれば、Accessのメモリリークによる強制終了を引き起こす。必ず`GoTo`による終了処理の一元化を行うこと。
Win32 APIの活用による「究極」
もし、Access標準の`SysCmd`では表現力が足りない場合(例:プログレスバーを独自フォームで浮かせたい場合)、`SetWindowPos` APIを用いて「常に最前面」の進捗バーを構築する手もある。だが、業務システムにおいては「標準機能の限界を理解し、標準機能で実装すること」こそが、後任者が保守できる唯一の道であると知れ。
レガシー環境との対話
古い環境では、`AbsolutePosition`が-1を返すようなカーソルタイプを選択しているケースがある。その場合は、`Recordset`の`Clone`を作成し、`Bookmark`で現在位置を制御するなどの代替手段を講じよ。
—
結びに代えて:エンジニアの誇り
ユーザーは、進捗バーが動いているだけで安心する。たとえ裏で何をしているか理解できなくても、「動いている」という事実は、システムに対する信頼そのものだ。
完璧なコードとは、単に動くコードのことではない。「次に触る誰かが、その意図を瞬時に理解できるコード」のことだ。今回の`SysCmd`の活用法を、君のライブラリに組み込み、Accessというレガシーの海を、洗練されたアーキテクチャで支配してほしい。
健闘を祈る。
