【入門編】DAO.RecordsetのdbOpenSnapshotとdbOpenDynaset:メモリ消費量とパフォーマンスのトレードオフ – Access VBA解析バイブル

スポンサーリンク

こんにちは!Access VBAの世界へようこそ。
マクロの記録ボタンをポチポチ押していた時代を卒業し、「自分の手でシステムをコントロールしたい!」と一歩を踏み出したあなたへ。

今回は、Access VBAのパフォーマンスとメモリ管理の要(かなめ)である、DAO.Recordsetの「dbOpenSnapshot」と「dbOpenDynaset」の使い分けについて徹底解説します。

「動けば何でもいいや」と適当にコードを書いていると、データが何万件と膨れ上がった時に「あれ?画面が固まった…」「PCのファンが爆音をあげた…」という恐怖の洗礼を受けることになります。

でも、大丈夫。ここをクリアすれば、あなたの書くVBAは見違えるほど軽快になり、プログラミングの構造本質がグッと見えてきますよ。さあ、一緒に極限の知見へと足を踏み入れましょう!

1. なぜRecordsetのタイプ選択が重要なのか?

Accessでデータを操作する際、私たちはVBAからデータベースのテーブルやクエリへアクセスするために `Recordset(レコードセット)` というオブジェクトを使います。

このレコードセットを作る際、Access(正確にはデータベースエンジンのACE/Jet)に対して「これからこのデータをどう扱う予定か?」を伝えなければなりません。

ここで選択肢になるのが、主に以下の2つです。

1. `dbOpenSnapshot` (スナップショット)
2. `dbOpenDynaset` (ダイナセット)

この選択を誤ることは、「ただ景色を見に行くだけのドライブなのに、巨大な引っ越しようトラックを借りてしまう」ようなものです。無駄にメモリを食いつぶし、処理速度をドロドロに低下させる原因になります。

2. 特性の比較:スナップショット vs ダイナセット

まずは、それぞれの性格をスッキリ整理しておきましょう。

| 項目 | `dbOpenSnapshot` (読み取り専用) | `dbOpenDynaset` (更新可能) |
| :— | :— | :— |
| 主な用途 | データの集計、印刷、画面への表示、検索 | データの追加、編集、削除(CRUD操作) |
| データの鮮度 | 取得した瞬間の「静止画」(固定) | 他のユーザーの変更がリアルタイムに反映される「動画」 |
| メモリ消費量 | 極めて軽量 (ローカルメモリに結果をキャッシュ) | 重め (レコードの変更を追跡するための管理領域が必要) |
| 速度 | 高速 | やや低速(トラッキングコストのため) |

💡 先輩からのアドバイス:基本は「スナップショット」

実務において、VBAで開くレコードセットの8割〜9割は「見るだけ(読み取り専用)」です。
つまり、特別な理由がない限りは、メモリに優しく高速な `dbOpenSnapshot` を選ぶのがプロの鉄則です。

3. 実践!コードで見る挙動の違いと書き方

百聞は一見にしかず。実際のコードを見てみましょう。
ここでは、10万件の受注データから「特定の顧客の売上合計を計算する(=データを変更しない)」というシナリオを想定します。

パターンA:正しい選択(dbOpenSnapshot)

Sub CalculateSales_Snapshot()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim totalSales As Currency

Set db = CurrentDb

‘ 【重要】見るだけなので dbOpenSnapshot を指定!
‘ これにより、余計な更新管理コストをカットし、メモリを節約します。
Set rs = db.OpenRecordset(“SELECT FROM T_受注明細 WHERE 顧客ID = ‘C001′”, dbOpenSnapshot)

totalSales = 0

‘ レコードが空でないかチェック
If Not rs.BOF Then
rs.MoveFirst
Do Until rs.EOF
totalSales = totalSales + rs!販売金額
rs.MoveNext
Loop
End If

MsgBox “合計売上: ” & Format(totalSales, “Standard”), vbInformation

‘ 後片付け(メモリ解放の作法)
rs.Close
Set rs = Nothing
Set db = Nothing
End Sub

パターンB:書き込みが必要な場合(dbOpenDynaset)

今度は、取得したデータのステータスを「処理済み」に書き換える(更新する)ケースです。ここでは `dbOpenDynaset` が主役に躍り出ます。

Sub UpdateStatus_Dynaset()
Dim db As DAO.Database
Dim rs As DAO.Recordset

Set db = CurrentDb

‘ 【重要】データを書き換えるので dbOpenDynaset を指定!
Set rs = db.OpenRecordset(“SELECT FROM T_受注明細 WHERE 処理フラグ = 0”, dbOpenDynaset)

Do Until rs.EOF
‘ 編集モードへ移行
rs.Edit

‘ データを書き換え
rs!処理フラグ = 1
rs!更新日時 = Now()

‘ 変更を確定(データベースに書き戻す)
rs.Update

rs.MoveNext
Loop

MsgBox “すべてのデータの更新が完了しました。”, vbInformation

‘ 後片付け
rs.Close
Set rs = Nothing
Set db = Nothing
End Sub

4. 陥りやすい罠:「見るだけなのに Dynaset を使ってしまった」時の末路

初学者の頃によくやってしまうのが、「どのタイプを指定していいか分からないから、とりあえずデフォルト(引数なし、または `dbOpenDynaset`)にしておこう」という妥協です。

もし数万件、数十万件といった実務レベルのデータ量に対して、読み取り目的であるにもかかわらず `dbOpenDynaset` を使い続けると、以下のような弊害が生まれます。

1. メモリリークとパフォーマンス劣化
Accessは「このデータ、後で書き換えられるかもしれない」と身構え、すべてのレコードに対するロックや変更追跡の仕組みを裏で維持し続けます。これがメモリを圧迫し、処理スピードがガクンと落ちます。
2. 思わぬロック競合
マルチユーザー環境(社内の複数人が同時にAccessを使っている状態)において、必要以上にDynasetでテーブル全体を占有すると、他のユーザーの入力作業を邪魔してしまい、システム全体がフリーズする原因になります。

5. 今回のまとめ

ここまでのポイントをギュッと凝縮します。

  • データを「読むだけ(集計・表示・検索)」なら迷わず `dbOpenSnapshot`! (圧倒的に速くて軽い)
  • データを「追加・編集・削除」するなら `dbOpenDynaset`! (変更を追跡できる賢い子)
  • 使い終わったレコードセットは、必ず `.Close` し、`Set … = Nothing` でメモリからキレイに消し去る!(オブジェクトのライフサイクル管理)

この原則を守るだけで、あなたの書くAccess VBAは「重くて使い物にならないマクロ」から、「プロ顔負けの高速な業務システム」へと生まれ変わります。

ここをクリアすれば、Access VBAの基礎はもうバッチリです!
自信を持って、次のステップへ進んでいきましょう。それでは、また次回の極限知見でお会いしましょう!

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