【実務・中級編】DAO.Recordsetの「Clone」と「Bookmark」で実現する、非連結フォームでの高速検索UI – Access VBA解析バイブル

スポンサーリンク

Access VBAを掌握する極限の知見:RecordsetCloneとBookmarkがもたらす「真の高速検索UI」の極意

開発現場でよく見かける悪夢がある。
ユーザーがテキストボックスに文字を入力するたびに、フォームの `RecordSource` を `SELECT FROM T_Table WHERE Field LIKE ‘…’` と書き換え、盛大にクエリを再評価し、フォーム全体をリフレッシュする――。

データ量が数千件程度ならごまかせても、数万件を超えた瞬間にUIはフリーズし、カクつき、ユーザーのイライラは最高潮に達する。Accessはクライアントサーバーシステムではない。テーブルの規模やネットワークの帯域(特にバックエンドをファイルサーバに置いたsplit DB構成)によっては、この「レコードソース直書き換え地獄」は致命傷になる。

今回は、Accessオブジェクトモデルの深淵を覗き、DAOの `RecordsetClone` と `Bookmark` を駆使して「一瞬で検索し、フォームの描画コストを極限までゼロにする」ための設計思想とプロダクションコードを伝授する。

なぜ `RecordSource` の書き換えは悪手なのか?

フォームの `RecordSource` を動的に変更することは、一見すると直感的で簡単な実装に見える。しかし、以下の重いコストを毎回支払っていることを自覚しなければならない。

1. クエリの再コンパイルと実行計画の破棄:Jet/ACEエンジンは、SQL文が変更されるたびに実行計画を再構築する。
2. ネットワークトラフィックの肥大化:バックエンド(別ファイルのAccess)との間で不必要なデータフェッチが発生する。
3. フォーム全体の再描画(Repaint):コントロールのハンドルが再生成され、ちらつき(フリッカー)の原因になる。

解決策:メモリ上のクローンを操作し、表示位置だけを同期させる

Accessのフォームには、あらかじめ元となるテーブルやクエリをバインド(連結)させておく。そして、ユーザーが検索を行う際は、フォームが裏で保持しているデータセットの複製(`RecordsetClone`)に対してメモリ上で高速な検索を行い、一致したレコードの「位置(`Bookmark`)」だけをフォーム本体に教え込む。

このアプローチであれば、データベースへの余計なアクセスは発生せず、フォームのレコードソースは一切汚さない

アーキテクチャの全体像

今回構築する検索UIのメカニズムは以下の通りだ。

1. フォームオープン時:フォームはすでにレコードを保持している。
2. 検索ボックスでの入力(`Change` イベント)

  • `RecordsetClone` を取得する。
  • DAOの `FindFirst` メソッドを使い、インデックスを活用した超高速検索を行う。
  • 一致が見つかったら、`RecordsetClone.Bookmark` をフォームの `Bookmark` プロパティに代入する。
  • フォームの表示位置が一瞬でそのレコードにジャンプする。

これだけだ。だが、この「シンプルさ」の裏に、実務で絶対に外せない堅牢なエラーハンドリングとライフサイクル管理の知見が詰まっている。

プロダクションコード:コピペで即導入可能な高速検索モジュール

以下のコードは、顧客マスタフォーム(例: `F_Customers`)を想定し、検索キーワードボックス(`txtKeyword`)に入力された文字列に部分一致するレコードへ瞬時にジャンプする実装である。

Option Compare Database
Option Explicit

‘ ==============================================================================
‘ フォーム名: F_Customers
‘ 概要: RecordsetCloneとBookmarkを活用した高速検索UIの実装
‘ ==============================================================================

Private Sub txtKeyword_Change()
On Error GoTo ErrorHandler

Dim rst As DAO.Recordset
Dim searchKeyword As String
Dim criteria As String

‘ 1. 入力値の取得とサニタイジング
‘ Changeイベントでは .Text プロパティを使用する(ActiveControlの特性に注意)
searchKeyword = Nz(Me.txtKeyword.Text, “”)

‘ 未入力の場合は検索を行わない(全解除等の仕様にする場合はここで分岐)
If Len(searchKeyword) = 0 Then Exit Sub

‘ 2. フォームのRecordsetCloneを取得
‘ ※ 注意: Cloneメソッドは「新規のRecordsetオブジェクト」を返すため、
‘ 使用後は必ず .Close と Set Nothing が必要。メモリリークの温床になる。
Set rst = Me.RecordsetClone

‘ 3. 検索条件の構築 (LIKE演算子とワイルドカードのエスケープに配慮)
‘ ※ 実務ではシングルクォートなどのエスケープ処理を関数化しておくことが望ましい
criteria = “[CompanyName] LIKE ‘” & Replace(searchKeyword, “‘”, “””) & “‘”

‘ 4. DAOの高速検索実行
rst.FindFirst criteria

‘ 5. 検索結果の判定とBookmarkの同期
If Not rst.NoMatch Then
‘ 該当レコードが存在する場合、フォームのBookmarkを同期させる
‘ これにより、フォームの表示位置が瞬時にジャンプする(再クエリは走らない)
Me.Bookmark = rst.Bookmark
Else
‘ 見つからなかった場合のハンドリング(必要に応じてステータスバー等に通知)
‘ 音を鳴らす、あるいは視覚的フィードバック(背景色変更など)
DoCmd.Beep
End If

CleanUp:
‘ 6. オブジェクトの解放(極めて重要)
If Not rst Is Nothing Then
rst.Close
Set rst = Nothing
End If
Exit Sub

ErrorHandler:
‘ 予期せぬエラーの捕捉
MsgBox “検索処理中にエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “システムエラー”
Resume CleanUp
End Sub

現場のプロが教える「陥りがちな罠」と回避策

このコードをそのまま現場に導入するにあたり、Access特有の仕様と罠についていくつか知見を共有しておこう。

1. `Change` イベントと `.Text` プロパティの罠

VBAのコントロールイベントで `Change` を使う場合、現在の入力値を取得するには `Me.txtKeyword.Value` ではなく `Me.txtKeyword.Text` を使わなければならない。`.Value` はフォーカスを失うまで更新されないためだ。
さらに、`Text` プロパティにアクセスする際は、コントロールにフォーカスがあることが大前提となる。もし他のプロシージャから制御を飛ばすような設計にする場合は注意が必要である。

2. DAO.Recordset のメモリリーク

初学者が最もやりがちなミスが、`Set rst = Me.RecordsetClone` と記述したまま、プロシージャの終了時に `rst.Close` と `Set rst = Nothing` を怠るケースだ。
Access VBAのガベージコレクションは完璧ではない。これを怠ると、フォームを開き閉めするたびにメモリ上にゾンビオブジェクトが蓄積し、や労働時間の後半に「メモリ不足」や「Accessの強制終了」という致命的なバグを引き起こす。取得したレコードセットは必ず自分で閉じて破棄しろ。

3. インデックスの効かない検索(ワイルドカードの先頭一致・完全一致)

今回のコードでは `LIKE ‘keyword’` (中間一致)を使っている。
データベースの仕様上、先頭にワイルドカード(“)を付与する検索は、テーブルのインデックススキャンをバイパスし、フルテーブルスキャン(全件走査)を強制する。数百万件クラスのデータであればこれでも重くなる。
もしパフォーマンスを極限まで追求する業務システムであれば、検索テキストボックスを「前方一致 (`keyword`)」または「完全一致」に設計し、対象フィールドにインデックスを張ること。これだけで体感速度は文字通り「異次元」になる。

まとめ:UIの滑らかさは、プロのエンジニアのプライドだ

動かない、遅い、カクつくAccessアプリの多くは、不適切なオブジェクトの再生成や、無駄なDBアクセスに起因している。

今回紹介した `RecordsetClone` と `Bookmark` を組み合わせた手法は、Accessの内部構造(Jet/ACEエンジン)の特性を深く理解した者だけが扱える、極めてエレガントな解決策だ。
「フォームのレコードソースを書き換えるな。動かすのはメモリ上のクローンとブックマークだ。」――この原則を胸に刻み、ユーザーがノンストレスで操作できる真のプロフェッショナルな業務システムを構築してほしい。

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