Access VBAを掌握する極限の知見:Form.RecordsetCloneで実現する「画面を揺らさない」超高速検索の極意
こんにちは。開発プロジェクトの現場において、Access VBAの限界を突破し、実用に耐えうる堅牢なシステムを構築し続けているチーフアーキテクトの私だ。
日々の開発で、こんなユーザーからの要望に頭を悩ませていないか?
「検索ボタンを押したときに、画面がカクついたり、ちらついたりするのを何とかしてほしい」
もし、検索のたびに `Me.Filter` と `Me.FilterOn = True` を叩いたり、毎回 `DoCmd.OpenForm` でWHERE条件を指定してフォームを再読み込みしているなら、今すぐその実装を捨ててほしい。それはAccessのオブジェクトモデルの挙動、そしてフォームのライフサイクルに対する理解不足から生じる「アンチパターン」だ。
今回は、Access VBAの真髄である `Form.RecordsetClone` を完全掌握し、バックグラウンドでミリ秒単位の検索・移動を実現する「プロフェッショナルな検索機能」の設計思想と実装コードを伝授する。
—
1. なぜ「フィルター再適用」や「再クエリ」は悪手なのか?
多くの初級~中級プログラマは、データを絞り込む際に以下のようなコードを書く。
‘ 【アンチパターン】絶対にやってはいけない実装
Private Sub btnSearch_Click()
‘ フィルターをかけてフォームを再描画する
Me.Filter = “CustomerName LIKE ‘” & Me.txtKeyword.Text & “‘”
Me.FilterOn = True
‘ これだと画面が再描画され、ちらつきが発生し、カレントレコードが先頭にジャンプしてしまう
End Sub
このアプローチの何が問題か。
1. 画面のちらつき(Flicker)と描画コスト: フォーム全体のUIコントロールが再描画されるため、視覚的なノイズとなりユーザー体験を著しく損なう。
2. カレントレコードの喪失: フィルター適用や `Me.Requery` を行うと、カレントレコードの位置情報がリセットされ、多くの場合先頭レコードに戻ってしまう。これを復元するために無駄なブックマークの退避・復元コードが必要になる。
3. ネットワーク負荷(特にACCDE/ACCDB分割構成): フォームのレコードソースを直接いじるアプローチは、バックエンドのデータベース(SQL Serverや共有フォルダ上のAccess)との間で余計なトラフィックを生む。
究極の解:RecordsetClone と Bookmark の同期
ここで登場するのが `Form.RecordsetClone` だ。
`RecordsetClone` は、フォームが現在開いているレコードソースの「クローン(複製)」をメモリ上に保持するDAO(またはADO)のレコードセットオブジェクトである。
このクローンは、フォームの画面上の描画(UI)と完全に切り離されている。
つまり、バックグラウンドのクローン側で高速に検索(`FindFirst`など)を行い、合致したレコードの位置(`Bookmark`)だけを、フォーム本体の `Me.Bookmark` にスワップ(代入)してやればいいのだ。
これにより、UIの再描画を最小限に抑え、画面を一切ちらつかせずに、一瞬で目的のレコードへジャンプさせることが可能になる。
—
2. 実務で耐えうる堅牢な設計と注意点
プロダクションコードとして組み込むにあたり、以下の設計原則を遵守してほしい。
1. ワイルドカードのエスケープとインジェクション対策: ユーザーが入力したキーワードに単一引用符(`’`)やワイルドカード文字が含まれていた場合のエラーハンドリングを怠らないこと。
2. 該当データなし(No Match)の優雅なハンドリング: `FindFirst` がヒットしなかった場合、エラーで落とすのではなく、ユーザーに分かりやすくトーストやメッセージで通知し、カレント位置を保持すること。
3. トランジェントなオブジェクトの解放: レコードセット操作におけるメモリリークを防ぐため、オブジェクト変数への代入と解放(`Set … = Nothing`)を徹底する。
—
3. コピペで使えるプロダクションコード
以下のコードは、顧客管理フォームを想定した、実務でそのまま使える堅牢な検索モジュールだ。テキストボックス `txtKeyword` に入力された文字列で、会社名(`CompanyName`)をあいまい検索し、該当レコードへ瞬時にジャンプする。
‘ ==============================================================================
‘ モジュール名: フォーム検索処理
‘ 概要 : RecordsetCloneとBookmarkを活用した高速・非チラつき検索
‘ ==============================================================================
Private Sub btnSearch_Async_Click()
Dim rs As DAO.Recordset
Dim searchCriteria As String
Dim keyword As String
‘ 1. 入力値の検証とサニタイジング
If Trim(Me.txtKeyword.Value & “”) = “” Then
MsgBox “検索キーワードを入力してください。”, vbExclamation, “入力エラー”
Me.txtKeyword.SetFocus
Exit Sub
End If
‘ SQLインジェクションや構文エラーを防ぐため、シングルクォートをエスケープ
keyword = Replace(Me.txtKeyword.Value, “‘”, “””)
On Error GoTo ErrorHandler
‘ 2. フォームのRecordsetCloneを取得
‘ ※ 注意: Form.RecordsetCloneを使用する場合、新規にDBを叩くのではなくメモリ上のクローンを参照する
Set rs = Me.RecordsetClone
‘ 3. 検索条件の構築(あいまい検索)
searchCriteria = “CompanyName LIKE ‘” & keyword & “‘”
‘ 4. バックグラウンドレコードセットで高速検索
rs.FindFirst searchCriteria
‘ 5. 検索結果の判定とフォームへの同期
If rs.NoMatch Then
MsgBox “該当するデータが見つかりませんでした。”, vbInformation, “検索結果”
Else
‘ ★ここが核心:フォームのBookmarkにクローンのBookmarkを渡すだけで、
‘ 画面を再読み込みすることなく、一瞬で対象レコードへジャンプする
Me.Bookmark = rs.Bookmark
End If
GoTo Finally
ErrorHandler:
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “システムエラー”
Finally:
‘ 6. オブジェクトの明示的な解放(メモリリーク防止)
If Not rs Is Nothing Then
rs.Close
Set rs = Nothing
End If
End Sub
—
4. チーフアーキテクトからの実践的アドバイス
このテクニックをマスターした君のフォームは、これまでのAccessアプリとは比べ物にならないほどキビキビと動くようになるだろう。
さらに実務をワンランク上の品質へ引き上げるために、以下の応用も検討してほしい。
- Enterキーでのインタラクション: テキストボックスの `KeyDown` イベントで `KeyCode = vbKeyReturn` の場合に上記の検索プロシージャを呼ぶようにすれば、マウスを使わずシームレスな検索が行える。
- 複数条件の拡張: `searchCriteria` の文字列結合部分を拡張すれば、日付範囲やコンボボックスの選択値(ステータス等)を組み合わせた複合検索にもそのまま流用可能だ。
Accessは、使い方を誤れば「重い・ダサい・壊れやすい」レガシーツールだが、オブジェクトモデルの挙動を熟知したエンジニアが設計すれば、現場の業務を爆速で効率化する最強のソリューションに変貌する。
妥協のないコードで、最高のプロダクトを組み上げてくれ。健闘を祈る。
