【テクニカル・上級編】【中級】DAO.Recordsetの「dbOpenSnapshot」と「dbOpenDynaset」:メモリ消費と速度のトレードオフ – Access VBA解析バイブル

スポンサーリンク

DAO.Recordsetの真実:`dbOpenSnapshot` と `dbOpenDynaset` のメモリと速度の限界調律

Access VBAのパフォーマンスチューニングにおいて、データベースエンジン(JET / ACE)の深層を理解しているか否かは、プロフェッショナルとアマチュアを分ける決定的な境界線となる。

多くの開発者は、データを取得する際に深く考えず `CurrentDb.OpenRecordset(“TableOrQuery”)` と記述し、デフォルトである `dbOpenDynaset` を使用している。しかし、これが大規模データ処理やネットワーク越しのバックエンド接続(FE/BE分離構成)において、どれほどの無駄なオーバーヘッドを生んでいるかを知る者は少ない。

今回は、DAO(Data Access Objects)における `dbOpenSnapshot` と `dbOpenDynaset` の挙動の違いを、メモリ管理、ロック機構、そしてカーソル維持のコストという観点から徹底的に解剖する。

1. 内部アーキテクチャの比較:なぜ Dynaset は重いのか?

まずは、両者の設計思想とメモリ上の振る舞いの違いを明確にする。

`dbOpenDynaset`(動的集合)

  • 役割: データの「読み取り」だけでなく「更新・追加・削除」を行うための双方向スクロール可能なカーソル。
  • 内部挙動:

1. クエリやテーブルの実行時、結果セットのレコードそのものではなく、「どのレコードが該当するか」を指し示すポインタ(PKeyなど)と、レコードの変更を追跡するためのロック情報をメモリ上に構築する。
2. 他のユーザーやプロセスによる変更をリアルタイムに反映させるため、常にロックテーブルやキャッシュとの同期を維持する。
3. ローカルの `.accdb` であっても、マルチユーザ環境ではページ単位のロック管理コストが発生し、FE/BE分離構成のネットワーク環境(SMB)では、パケットの往復が爆発的に増加する。

`dbOpenSnapshot`(静的スナップショット)

  • 役割: データの「読み取り専用(Read-Only)」に特化した固定ビュー。
  • 内部挙動:

1. クエリが実行された瞬間のデータのスナップショット(静的なコピー)をメモリまたは一時ファイル(Tempファイル)上に構築する。
2. 一度生成してしまえば、他のユーザがデータを改変しようが知ったことではない。ロックの維持も、リアルタイム同期の必要もない。
3. 単一方向(`MoveNext` のみ)に特化させることも可能であり、メモリのフットプリントを極限まで小さくできる。

2. メモリ消費とパフォーマンスのトレードオフ

「読み取り専用」の処理(レポート生成、集計、画面へのデータ転送、ログ出力など)において `dbOpenDynaset` を使うことは、武器を持たずに戦場に出るようなものだ。

以下の実測的知見を見てほしい。

1. ネットワークトラフィックの削減:
Dynasetはレコードの変更検知のためにJet/ACEエンジンと常に対話する。一方、Snapshotはデータを一括してローカルメモリ(または一時領域)に取り込むため、ネットワーク帯域の細い環境やVPN接続されたファイルサーバー上のAccessバックエンドに対して、最大で数倍から十数倍の速度差を生む。
2. メモリリークとガベージコレクションの罠:
VBAのランタイムはCOMベースであり、.NETのような洗練された世代別GCを持たない。`Recordset` オブジェクトを適切に `Close` し、変数に `Nothing` を代入してメモリを解放しない場合、Dynasetが保持していたロック情報やキャッシュはプロセスが終了するまでメモリ上に残留し続ける。

3. 実装コード:極限まで最適化されたデータ処理パターン

ここでは、数万件規模のレコードを高速に走査し、メモリを一切リークさせずに処理する模範的なプロシージャを示す。

Option Compare Database
Option Explicit

‘ ==============================================================================
‘ 処理名 : ProcessLargeDataSetOptimized
‘ 概要 : dbOpenSnapshotを用いた高速かつ安全なデータ読み取り処理
‘ 備考 : ネットワーク越しのバックエンドを想定し、通信コストとメモリを最小化
‘ ==============================================================================
Public Sub ProcessLargeDataSetOptimized()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim lngCounter As Long

‘ 現在のデータベースインスタンスを取得(CurrentDbプロパティの乱用を避ける)
Set db = CurrentDb()

On Error GoTo ErrorHandler

‘ 【重要】読み取り専用であれば必ず dbOpenSnapshot を指定する。
‘ さらに dbForwardOnly を併用することで、双方向ポインタの維持コストを完全に排除する。
Set rs = db.OpenRecordset( _
“SELECT FROM M_LargeDataMaster WHERE ProcessedFlag = 0”, _
dbOpenSnapshot, _
dbForwardOnly + dbReadOnly)

‘ レコードが存在しない場合のガード
If rs.EOF Then
Debug.Print “処理対象のレコードは存在しません。”
GoTo Cleanup
End If

‘ 高速イテレーション処理
lngCounter = 0
Do While Not rs.EOF
‘ — 実際のビジネスロジック(例:データの参照) —
‘ ※Snapshotのため、ここでの rs!FieldName へのアクセスはメモリ上のキャッシュから行われる
Dim targetValue As String
targetValue = Nz(rs!DataName, “”)

‘ 処理のシミュレーション
lngCounter = lngCounter + 1

‘ 次のレコードへ(ForwardOnlyのため戻ることはできないが最速)
rs.MoveNext
Loop

Debug.Print “処理完了: ” & lngCounter & ” 件のレコードを走査しました。”

Cleanup:
‘ ————————————————————————–
‘ オブジェクトの明示的解放(メモリリークの完全防止)
‘ ————————————————————————–
If Not rs Is Nothing Then
rs.Close
Set rs = Nothing
End If

If Not db Is Nothing Then
Set db = Nothing
End If

Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”
Resume Cleanup
End Sub

4. シニアアーキテクトが現場で使うべき選択基準

実務においてどのタイプを選択すべきか、以下のマトリクスを基準にコードレビューを行うべきである。

| 要件 / シチュエーション | 推奨する Recordset タイプ | 理由 |
| :— | :— | :— |
| 画面からのデータ編集(フォーム連携) | `dbOpenDynaset` | ユーザーの入力値をリアルタイムにテーブルへ反映・競合検知するため。 |
| 単なる一覧表示・検索(コンボ・リストボックス) | `dbOpenSnapshot` | 編集不要であり、スクロール速度とメモリ効率を優先するため。 |
| 集計・バッチ処理・帳票出力・ログ解析 | `dbOpenSnapshot` + `dbForwardOnly` | 変更を追跡する必要が一切なく、オーバーヘッドがゼロに近いため。 |
| トランザクションを伴う複雑な一括更新 | `dbOpenDynaset` または SQL実行 (`CurrentDb.Execute`) | 基本はSQL(UPDATE/INSERT)で処理すべきだが、オブジェクト操作が必要な場合のみDynaset。 |

5. 結び:チーフアーキテクトからの警鐘

Access VBAは「手軽に動かせるプロトタイピング言語」として誤解されがちだが、その下で稼働する JET / ACE データベースエンジンは、正しく調律すれば極めて堅牢で高速なトランザクション処理システムとなる。

「とりあえず動くから」という理由で全てのクエリを `dbOpenDynaset` で実装する悪習は、ネットワーク帯域を圧迫し、クライアントPCのメモリを蝕み、やがてシステム全体の寿命を縮める。

コードを書くその瞬間、「このデータは書き換える必要があるのか?それとも見るだけか?」を自問自答せよ。そのわずかな意識の差こそが、真にスケーラブルでプロフェッショナルなシステムを構築する唯一の道である。

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