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

スポンサーリンク

【Access VBA極限の知見】DAO.RecordsetのCloneとBookmarkが制する、非連結フォームの高速検索UI設計

開発現場でよく見かける光景がある。
「検索条件を入力するたびに `Me.Filter` と `Me.FilterOn = True` を叩きまくるフォーム」だ。

数万件程度のレコードであれば、ローカルのAccess(ACEエンジジン)であっても、フォームの再描画やレコードの再同期で画面がチラつき、ユーザーにストレスを与える。これがバックエンドにSQL ServerやOracleを据えたADPやODBC接続のプロジェクトであれば、検索のたびにネットワークラウンドトリップが発生し、実用に耐えない遅延を生む。

「なぜその書き方は非効率なのか?」
答えは単純だ。UIの描画とデータの走査を、同じ表舞台(フォームのレコードソース)で同時にやらせているからだ。

今回は、Access VBAの底力を限界まで引き出し、数百万件のデータすらミリ秒単位で捉える「DAO.RecordsetのCloneとBookmarkによる高速検索UI設計パターン」を伝授する。

1. アーキテクチャの核心:なぜ「Clone」と「Bookmark」なのか?

非連結フォーム、あるいは極限までパフォーマンスをチューニングしたフォームにおいて、検索UIの王道は以下のアーキテクチャだ。

1. フォームのレコードソースは固定する(あるいは軽量なクエリにしておく)。
2. バックグラウンドでメモリ上に保持している `DAO.Recordset` のクローン(`Clone`)に対して、高速な `FindFirst` や `FindNext` を実行する。
3. 一致するレコードが見つかったら、その一意の識別子である `Bookmark` を取得し、フォームの `Bookmark` プロパティに一撃で同期させる。

この設計が圧倒的に優れている理由

  • UI描画コストの最小化: フォームのレコードソース自体を頻繁に書き換えないため、カレントレコードの移動とピンポイントの同期のみで済み、画面のチラつきが完全に消滅する。
  • メモリ効率と速度: DAOの `Clone` メソッドは、データそのものを複製するのではなく、既存のレコードセットへのポインタ(参照)を共有する軽量なオブジェクトを生成する。そのため、メモリ消費を抑えつつ、独立したカレントレコード位置を持つことができる。
  • 堅牢性: ネットワークの遅延や排他制御の影響を受けにくく、マルチユーザー環境でも安全に動作する。

2. 実装パターン:プロダクションコード

以下のコードは、そのまま実際の業務システムに組み込めるレベルまで昇華させた実装例だ。
非連結のテキストボックス(`txtSearch`)にキーワードを入力し、リストボックスや詳細ビューのポインタを高速にジャンプさせるシナリオを想定している。

フォームモジュール側の実装

Option Compare Database
Option Explicit

‘ プライベート変数としてDAOレコードセットの参照を保持
Private m_rsClone As DAO.Recordset

Private Sub Form_Load()
On Error GoTo ErrorHandler

‘ 1. フォームのレコードソースに対するクローンを作成し、メモリ上に保持
‘ フォームの RecordsetClone は自動生成されるが、独自の検索用として明示的にクローンを作成する
Set m_rsClone = Me.Recordset.Clone

Exit Sub

ErrorHandler:
MsgBox “フォームの初期化に失敗しました: ” & Err.Description, vbCritical, “システムエラー”
End Sub

Private Sub Form_Unload(Cancel As Integer)
‘ 2. オブジェクトのライフサイクル管理:メモリリークを確実に防ぐ
If Not m_rsClone Is Nothing Then
m_rsClone.Close
Set m_rsClone = Nothing
End Sub
End Sub

‘ ==============================================================================
‘ 検索実行ボタン、またはテキストボックスのChangeイベントから呼び出すコアロジック
‘ ==============================================================================
Public Sub ExecuteHighSpeedSearch(ByVal SearchKeyword As String)
On Error GoTo ErrorHandler

If Trim(SearchKeyword) = “” Then
MsgBox “検索キーワードを入力してください。”, vbExclamation, “入力チェック”
Exit Sub
End Sub

‘ SQLインジェクションや構文エラーを防ぐため、エスケープ処理を適切に行う
‘ ここでは簡易的にシングルクォートのエスケープを実施
Dim sanitizedKeyword As String
sanitizedKeyword = Replace(SearchKeyword, “‘”, “””)

‘ 3. DAOの強力かつ高速な条件検索 (FindFirst)
‘ 例: 顧客名(CompanyName) 部分一致検索
Dim criteria As String
criteria = “CompanyName LIKE ‘” & sanitizedKeyword & “‘”

‘ クローン側で検索を実行(UIの描画を伴わないため圧倒的に高速)
m_rsClone.FindFirst criteria

‘ 4. 検索結果の判定とフォームへの同期
If m_rsClone.NoMatch Then
MsgBox “該当するデータは見つかりませんでした。”, vbInformation, “検索結果”
Else
‘ フォームのBookmarkをクローンのBookmarkに一致させる
‘ これにより、フォームの表示レコードが一瞬で該当位置へジャンプする
Me.Bookmark = m_rsClone.Bookmark
End If

Exit Sub

ErrorHandler:
MsgBox “検索処理中に予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “エラー”
End Sub

3. 現場で絶対に外してはならない「3つの鉄則」

このパターンの運用において、プロのエンジニアが必ず押さえているインフラ的・設計的注意点を共有する。

① オブジェクトのライフサイクルとメモリリークの根絶

VBAにおけるDAOオブジェクトの解放漏れは、Accessの肥大化(Bloat)およびメモリリークの最大の温床となる。
フォームが閉じられる際(`Form_Unload`)には、必ず `Close` メソッドを呼び出し、変数に `Nothing` を代入してメモリを完全に解放すること。これを怠ると、長時間稼働する業務システムにおいて確実にAccessが不安定になる。

② インデックスの物理設計(Jet/ACEの限界を知る)

どれほどVBA側のコードを洗練させても、バックエンドのテーブル側に適切なインデックス(INDEX)が貼られていなければ、`FindFirst` はテーブル全体のフルスキャン(全件走査)を強いられる。
検索条件に指定するカラム(上記の例では `CompanyName` など)には、必ずローカルデータベースであればインデックスを付与し、外部DB(SQL Server等)であればパススルーや適切なキー設計を行っておくこと。

③ 複数ユーザー環境における「ブックマークの無効化」

`Bookmark` は、あくまでその瞬間のレコードセット内での「物理的・論理的な位置」を指すバイト配列である。
もし他ユーザーが該当レコードを削除した場合、あるいは自身のレコードセットが再クエリ(`Requery`)された場合、既存の `Bookmark` は無効となり、ランタイムエラー(実行時エラー 3159: 「無効なブックマークです」)が発生する。
プロダクションコードでは、必ず `On Error` によるトラップを仕掛け、エラーが発生した場合はレコードセットの再取得(リフレッシュ)を行うフェールセーフ構造を組み込んでおくべきだ。

総括

Access VBAは、レガシーな言語として片付けられがちだ。しかし、オブジェクトモデルの挙動、メモリ管理、そしてDAOの特性を深く理解したエンジニアが操れば、モダンなデスクトップアプリケーションに匹敵する軽快なUIを叩き出すことができる。

「フォームが重い」「検索が遅い」と嘆く前に、UIとデータのライフサイクルを切り離せ。
`Clone` と `Bookmark` を使いこなした瞬間から、あなたの作るAccessアプリは見違えるほどプロフェッショナルな挙動へと生まれ変わるはずだ。

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