【入門編】DAOの「dbOpenDynaset」と「dbOpenSnapshot」:用途別パフォーマンス最適化の境界線 – Access VBA解析バイブル

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

「なんとなく」で選ばない。Access VBAの心臓部、DAOの「開き方」でパフォーマンスを支配する

こんにちは。Access VBAの世界へようこそ。
マクロの記録から一歩踏み出し、自分でコードを書き始めたあなたは、すでに「自動化エンジニア」への第一歩を力強く踏み出しています。素晴らしいですね。

さて、コードを書き進めていくと、必ずと言っていいほど遭遇する壁があります。それは「動作が重い」「なぜかエラーが出る」「他の人が使っているときにロックがかかる」といった問題です。

これらは、実はプログラムの書き方以前に、「データの開き方(Recordsetのタイプ)」を間違えていることが原因であるケースが非常に多いのです。

今日は、DAO(Data Access Objects)における二大巨頭、`dbOpenDynaset` と `dbOpenSnapshot` の違いを、アーキテクチャの視点から徹底的に解剖していきます。ここをクリアすれば、あなたの作るシステムは「動くだけのツール」から「プロ仕様のアプリケーション」へと昇華しますよ。

1. イメージで理解する:スナップショットとダイナセット

難しい技術用語の前に、まずは直感的なイメージを持っておきましょう。

`dbOpenSnapshot`(スナップショット)は「写真」

ある瞬間のデータをパシャリと撮影した「写真」をイメージしてください。
写真は眺めることができますが、写真の中の人物の服の色を変えることはできませんよね?
データも同じです。「その瞬間の状態を切り取ったもの」なので、読み取りは爆速ですが、そのデータを使って書き込みを行うことはできません。

`dbOpenDynaset`(ダイナセット)は「ライブ中継」

こちらは、今まさに目の前で起きている出来事を映し出す「ライブ映像」です。
映像の中で誰かが動けば、それもリアルタイムに反映されます。さらに、映像を見ながら「あ、あの人が持っているものをこっちに渡して」と指示(データの更新)を出すことも可能です。
「生きたデータ」に直接アクセスするため、読み書きの両方ができますが、ライブ中継を維持するためのコスト(負荷)がかかります。

2. アーキテクチャの深層:なぜ速度に差が出るのか?

エンジニアとして、一歩踏み込んだ「裏側の動き」を知っておきましょう。ここが、単なるマクロ使いと、設計ができるエンジニアの境界線です。

特徴 `dbOpenSnapshot` (スナップショット) `dbOpenDynaset` (ダイナセット)
データの性質 静的(読み取り専用) 動的(更新可能)
メモリ消費 非常に少ない。データの一時コピーを作るだけだから。 比較的多い。常にテーブルとの接続を維持するため。
処理速度 圧倒的に速い。読み取ることに特化している。 やや遅い。更新のための管理情報が必要だから。
ロックの影響 受けにくい。読み取り専用なので、他の操作を邪魔しない。 受けやすい。書き込み準備のためにレコードを保持するため。

なぜSnapshotは速いのか?

Snapshotは、クエリの結果をメモリ上のバッファにガサッと放り込みます。一度コピーを取ってしまえば、元のテーブルがどう変わろうが関係ありません。そのため、データベースエンジンは「読み取り」だけに全力を注げます。

なぜDynasetは重くなるのか?

Dynasetは、レコードセットの各行が「テーブルのどの位置にあるか」というポインタ(参照情報)を常に保持しています。さらに、あなたがデータを書き換えようとしたとき、即座にテーブルへ反映できるよう、複雑な管理プロセスが裏側で動いています。これが「重さ」の正体です。

3. 実践:使い分けの黄金律

「じゃあ、どっちを使えばいいの?」という疑問にお答えしましょう。結論はシンプルです。

1. 「表示するだけ」「集計するだけ」なら、迷わず `dbOpenSnapshot` を選ぶ。

  • レポートの出力、コンボボックスのリスト作成、データの検索表示など。

2. 「値を書き換える」「レコードを追加・削除する」なら、`dbOpenDynaset` を選ぶ。

  • 入力フォームでの編集、一括更新処理など。

4. 実装コード:プロの書き方

それでは、実際のコードを見てみましょう。現場でそのまま使える、エラーハンドリング(エラーが起きた時の備え)を組み込んだプロフェッショナルな書き方です。

Option Compare Database
Option Explicit

' =================================================================
' 目的: 用途に合わせたRecordsetの使い分けデモンストレーション
' =================================================================

Sub DemonstrateRecordsetTypes()
    Dim db As DAO.Database
    Dim rsSnapshot As DAO.Recordset
    Dim rsDynaset As DAO.Recordset
    Dim strSql As String
    
    ' エラーハンドリングの設定
    On Error GoTo ErrorHandler

    Set db = CurrentDb
    strSql = "SELECT EmployeeID, EmployeeName, Salary FROM T_Employees"

    ' -------------------------------------------------------------
    ' パターン1: dbOpenSnapshot (読み取り専用・高速)
    ' 用途: データの表示、計算、レポート作成など
    ' -------------------------------------------------------------
    Debug.Print "--- Snapshot Mode Start ---"
    Set rsSnapshot = db.OpenRecordset(strSql, dbOpenSnapshot)
    
    Do Until rsSnapshot.EOF
        ' 高速に読み取りが可能
        Debug.Print "Name: " & rsSnapshot!EmployeeName & " | Salary: " & rsSnapshot!Salary
        rsSnapshot.MoveNext
    Loop
    
    ' ※注意: rsSnapshot!Salary = 500000 などの書き込みはエラーになります
    
    rsSnapshot.Close
    Set rsSnapshot = Nothing
    Debug.Print "--- Snapshot Mode End ---"


    ' -------------------------------------------------------------
    ' パターン2: dbOpenDynaset (更新可能・動的)
    ' 用途: データの更新、追加、削除など
    ' -------------------------------------------------------------
    Debug.Print "--- Dynaset Mode Start ---"
    Set rsDynaset = db.OpenRecordset(strSql, dbOpenDynaset)
    
    Do Until rsDynaset.EOF
        ' 条件に一致するレコードがあれば、給与をアップさせる
        If rsDynaset!Salary < 300000 Then
            rsDynaset.Edit             ' 編集モード開始
            rsDynaset!Salary = 350000  ' 値の書き換え
            rsDynaset.Update           ' 変更の確定
            Debug.Print rsDynaset!EmployeeName & " の給与を更新しました。"
        End If
        rsDynaset.MoveNext
    Loop
    
    rsDynaset.Close
    Set rsDynaset = Nothing
    Debug.Print "--- Dynaset Mode End ---"

CleanUp:
    ' 後片付け(オブジェクトの解放はエンジニアの嗜みです)
    On Error Resume Next
    If Not rsSnapshot Is Nothing Then rsSnapshot.Close: Set rsSnapshot = Nothing
    If Not rsDynaset Is Nothing Then rsDynaset.Close: Set rsDynaset = Nothing
    If Not db Is Nothing Then Set db = Nothing
    Exit Sub

ErrorHandler:
    MsgBox "エラーが発生しました。" & vbCrLf & _
           "番号: " & Err.Number & vbCrLf & _
           "内容: " & Err.Description, vbCritical, "実行エラー"
    Resume CleanUp
End Sub

コードのポイント解説

  • `On Error GoTo ErrorHandler`: プログラムが予期せぬ事態(データが空だった、ロックされていた等)で止まらないようにするための必須テクニックです。
  • `rs.Edit` と `rs.Update`: Dynasetでデータを書き換えるときは、必ず「今から編集しますよ(`Edit`)」と宣言し、「書き換えましたよ(`Update`)」と確定させる必要があります。これを忘れると、データは更新されません。
  • `CleanUp` セクション: ここが重要です。エラーが起きても起きなくても、開いたRecordsetを確実に閉じる(`Close`)ことで、メモリリークやデータベースのロックを防ぎます。

5. まとめ:次の一歩へ

いかがでしたでしょうか?

  • 速さと軽さを求めるなら `dbOpenSnapshot`
  • 書き換えが必要なら `dbOpenDynaset`

この使い分けができるだけで、あなたの作るAccessアプリケーションの安定性とスピードは劇的に向上します。

「マクロの記録」は、あくまで「やりたいことのヒント」です。その裏側で、どのようなオブジェクトが、どのようなモードで動いているのか。その「重み」を感じ取れるようになれば、あなたはもう立派なエンジニアの仲間入りです。

ここをクリアすれば、Access VBAの基本はバッチリですよ。
自信を持って、次のコードを書き進めてくださいね。応援しています!

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