【実務・中級編】DAOのRecordsetCloneとブックマーク:サブフォームとの同期を完璧に制御するUI操作の極意 – Access VBA解析バイブル

スポンサーリンク
スポンサーリンク

「RecordsetClone」と「Bookmark」を制する:Access VBAでサブフォーム同期を完璧に制御する設計思想

Access VBAで業務ツールを開発している者が必ず一度はぶつかる壁がある。それが「メインフォームとサブフォーム、あるいは複数のフォーム間でのレコード同期」だ。

初心者はここで、`DoCmd.GoToRecord` や `Filter` プロパティを多用し、画面の激しいチラつき(フリッカー)と、予期せぬデータ更新エラーに悩まされる。あるいは、ADOで別途クエリを投げてIDで再検索し、パフォーマンスを著しく低下させる。

断言しよう。フォーム上のレコード制御に `Filter` や `Requery` を乱用するのは三流の設計だ。

プロが選ぶ最適解は、DAOの `RecordsetClone` と `Bookmark` の組み合わせである。なぜこの手法が「極限の正解」なのか。そのアーキテクチャ的な理由と、堅牢な実装パターンを伝授する。

1. アーキテクチャの本質:なぜ RecordsetClone なのか?

まず、フォームの `Recordset` と `RecordsetClone` の違いを理解せよ。

通常、フォームがデータを表示しているとき、そこには「現在のレコードポインタ」が存在する。もし、コードから直接 `Me.Recordset.FindFirst` を実行してレコードを移動させると、ユーザーが操作中のカーソルが強制的に移動し、イベント(`On Current` など)が連鎖的に発生する。 これが、無限ループや意図しない挙動、UIのガタつきを招く原因だ。

RecordsetClone という「影のコピー」

`RecordsetClone` は、フォームが保持しているレコードセットの「ポインタだけのコピー」である。

  • 実データは共有しているため、メモリ消費は極めて少ない。
  • ポインタ(現在位置)は独立している。 つまり、クローン側でどれだけレコードを検索して移動させても、ユーザーに見えているフォーム上の選択行は一切動かない。

この「影のコピー」で目的のレコードを特定し、その「しおり(Bookmark)」だけを本尊(フォーム)に渡す。これが、UIを一切乱さずにレコードを同期させる唯一のスマートな方法だ。

2. 実装の極意:ブックマーク同期のメカニズム

同期のフローは極めてシンプルに設計すべきだ。

1. クローンを作成する (`Me.RecordsetClone`)
2. クローン側で検索をかける (`.FindFirst`)
3. 見つかった場合、その `Bookmark` をフォームの `Bookmark` に代入する

これにより、フォームは内部的に「あぁ、このレコードの位置にジャンプすればいいのだな」と理解し、瞬時に表示を切り替える。

プロダクションレベルの実装コード

以下に、メインフォームとサブフォームの間でレコードを完璧に同期させるための、再利用可能な実装例を示す。

Option Compare Database
Option Explicit

' ======================================================================================
' 目的:サブフォームで選択したレコードをメインフォームに同期させ、
'       あるいはその逆を制御するための堅牢な同期ロジック
' ======================================================================================

''' <summary>
''' 指定したフォームのレコードを、特定の条件で同期させる
''' </summary>
''' <param name="targetForm">同期させたい対象のフォームオブジェクト</param>
''' <param name="criteria">FindFirstに渡す検索条件(例: "CustomerID = 123")</param>
Public Sub SyncRecordPointer(ByRef targetForm As Form, ByVal criteria As String)
    On Error GoTo Err_Handler

    Dim rs As DAO.Recordset
    
    ' 1. フォームのレコードセットのクローンを取得
    '    これにより、UI側のカーソルを動かさずに検索が可能になる
    Set rs = targetForm.RecordsetClone

    ' 2. クローン内で条件に合致するレコードを検索
    rs.FindFirst criteria

    ' 3. レコードが見つかった場合のみ、ブックマークを同期
    If Not rs.NoMatch Then
        ' ここが極意:クローンのしおりをフォーム本体にコピーする
        ' これにより、最小限のコストで表示レコードが切り替わる
        targetForm.Bookmark = rs.Bookmark
    Else
        ' 見つからなかった場合のハンドリング(業務仕様に合わせて実装)
        Debug.Print "指定されたレコードが見つかりませんでした: " & criteria
    End If

CleanUp:
    ' オブジェクトのライフサイクル管理を徹底せよ
    If Not rs Is Nothing Then
        rs.Close
        Set rs = Nothing
    End If
    Exit Sub

Err_Handler:
    MsgBox "レコード同期エラー: " & Err.Description, vbCritical
    Resume CleanUp
End Sub

' --------------------------------------------------------------------------------------
' 実装例:サブフォームのレコード移動時にメインフォームを同期させる
' --------------------------------------------------------------------------------------
Private Sub Form_Current()
    ' サブフォーム側でこのイベントを実装し、メインフォームを同期させる
    ' 例:メインフォームが「frmMain」で、共通キーが「OrderID」の場合
    
    Dim strCriteria As String
    strCriteria = "OrderID = " & Me.OrderID
    
    ' メインフォームに対して同期処理を実行
    ' ※呼び出し元がサブフォームであるため、親フォーム(Parent)を指定
    Call SyncRecordPointer(Me.Parent, strCriteria)
End Sub

3. チーフアーキテクトからの設計上の注意点

このコードを単にコピペすれば動くが、現場で「バグを出さない」ためには以下の3点を意識せよ。

① データ型の整合性に厳格であれ

`FindFirst` に渡す文字列(`criteria`)の構築ミスは、Access VBAで最も多いバグの一つだ。

  • 数値型の場合: `”ID = ” & Me.ID`
  • 文字列型の場合: `”Name = ‘” & Me.Name & “‘”` (シングルクォートで囲む)
  • 日付型の場合: `”Date = #” & Format(Me.Date, “yyyy-mm-dd”) & “#”` (シャープで囲む)

ここを曖昧にすると、実行時エラーではなく「レコードが見つからない」というサイレントバグになり、調査コストを増大させる。

② 「Dirty」状態の罠を回避せよ

レコードを同期させる際、現在編集中のレコードがある(`Me.Dirty = True`)状態でポインタを移動させると、Accessは自動的にその変更を保存しようとする。
意図しない保存を防ぎたい場合は、同期前に `If Me.Dirty Then Me.Undo` または `Me.Dirty = False` を明示的に制御せよ。

③ パフォーマンスの重みを理解せよ

`RecordsetClone` は高速だが、`FindFirst` はインデックスが貼られていないフィールドに対して実行すると、レコード数が増えた際に目に見えて遅くなる。
同期に使用するキー(ID等)には、必ずテーブル設計段階で「インデックス(重複なし)」を設定しておくこと。 これができないエンジニアは、コードではなく設計で負けている。

まとめ:支配せよ、ポインタを。

`Filter` でレコードを絞り込むのは、データの「抽出」である。
`Bookmark` でレコードを移動させるのは、データの「参照」である。

UI操作における同期は、あくまで「参照」の切り替えで完結させるべきだ。`RecordsetClone` を使いこなし、ユーザーにストレスを与えない、静寂で高速なインターフェースを構築せよ。それが、プロフェッショナルな業務アプリケーションの証である。

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