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

スポンサーリンク

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というレガシーの海を、洗練されたアーキテクチャで支配してほしい。

健闘を祈る。

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