なぜあなたのフォームは重いのか?DAOのカーソルタイプを使いこなして爆速化する極意
「マクロの記録から一歩踏み出して、VBAで本格的なツールを作りたい!」
そう意気込んでコードを書き始めたあなた、素晴らしい一歩です。
しかし、いざ複雑なデータを表示しようとしたとき、こんな経験はありませんか?
「フォームを開くたびに、数秒間マウスカーソルがぐるぐる回って、動作が止まってしまう……」
実はこれ、プログラムの書き方が悪いのではなく、「データの持ち方(カーソルタイプ)」の選択ミスが原因かもしれません。
今日は、Access VBAの心臓部とも言える「DAO(Data Access Objects)」における、2つの魔法の選択肢、`dbOpenDynaset` と `dbOpenSnapshot` の違いについて、その本質を解き明かしていきましょう。ここをクリアすれば、あなたの作るアプリケーションの品質は劇的に向上しますよ。
1. 「動的な映像」か、「静止画」か。
DAOでレコードセット(データの塊)を読み込むとき、私たちは「どういう状態でデータを掴むか」を指示する必要があります。これを「カーソルタイプ」と呼びます。
イメージしやすいように、例え話で説明しましょう。
dbOpenDynaset(ダイナセット):ライブ配信の映像
`dbOpenDynaset` は、いわば「ライブ配信のビデオストリーミング」です。
今、目の前で見ているデータは、常にデータベースの「生の状態」とつながっています。
- 特徴: 誰かがデータを書き換えたら、すぐに反映される。自分自身でデータを書き換えることもできる。
- コスト: 常に「最新の状態」を監視し続ける必要があるため、メモリや通信の負荷が少し高い。
dbOpenSnapshot(スナップショット):写真
`dbOpenSnapshot` は、いわば「決まった瞬間の写真」です。
コマンドを実行した瞬間のデータを、パシャリと切り取ってメモリに保存します。
- 特徴: 一度撮った写真は変わりません。読み込みが非常に高速。ただし、その後に元のデータが変わっても、手元の写真は変わりません。また、基本的には「読み取り専用」です。
- コスト: 監視の必要がないため、非常に軽量で爆速。
| 特徴 | dbOpenDynaset (ダイナセット) | dbOpenSnapshot (スナップショット) |
|---|---|---|
| 用途 | データの入力・編集・更新 | データの参照・検索・一覧表示 |
| 更新の反映 | リアルタイムに反映される | 読み込んだ時点のデータで固定 |
| 書き込み | 可能 | 基本不可(読み取り専用) |
| 動作速度 | 普通 | 圧倒的に速い |
2. 実践:フォームの読み込み速度を劇的に変えるコード
では、実際にどのように使い分けるべきか、コードを見てみましょう。
「用途に応じて、最適なカーソルを返してくれる関数」を作るのが、プロの設計です。
''' <summary>
''' 用途に応じて最適なRecordsetを取得する関数
''' </summary>
''' <param name="strSQL">実行するSQL文</param>
''' <param name="isEditMode">編集モードにするかどうか</param>
''' <returns>DAO.Recordset オブジェクト</returns>
Public Function GetSmartRecordset(ByVal strSQL As String, ByVal isEditMode As Boolean) As DAO.Recordset
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim cursorType As Long
Set db = CurrentDb
' --- ここがアーキテクトの判断ポイント ---
If isEditMode Then
' 編集が必要なら、常に最新の状態を追える「ダイナセット」を選択
cursorType = dbOpenDynaset
Else
' 閲覧のみなら、メモリ負荷が低く爆速な「スナップショット」を選択
cursorType = dbOpenSnapshot
End If
' ---------------------------------------
On Error GoTo ErrorHandler
' 指定したカーソルタイプでレコードセットを開く
Set rs = db.OpenRecordset(strSQL, cursorType)
' 関数に結果をセットして返す
Set GetSmartRecordset = rs
Exit Function
ErrorHandler:
MsgBox "エラーが発生しました: " & Err.Description, vbCritical
Set GetSmartRecordset = Nothing
End Function
''' <summary>
''' フォーム読み込み時のテスト用プロシージャ
''' </summary>
Sub TestLoading()
Dim rs As DAO.Recordset
Dim strSql As String
strSql = "SELECT * FROM T_売上データ"
' パターンA:一覧表示フォーム(読み取り専用)
' スナップショットを使うので、大量データでも一瞬で開く!
Set rs = GetSmartRecordset(strSql, False)
Debug.Print "閲覧モードで開きました。"
' 使い終わったら必ず閉じる(メモリリーク防止の鉄則!)
rs.Close
Set rs = Nothing
' パターンB:入力フォーム(編集あり)
' ダイナセットを使うので、データの更新が可能!
Set rs = GetSmartRecordset(strSql, True)
Debug.Print "編集モードで開きました。"
rs.Close
Set rs = Nothing
End Sub
3. 初心者が陥りやすい「3つの罠」
ここからは、現場でよく見かける「痛いミス」を共有しておきますね。これを知っているだけで、デバッグの時間は半分になります。
罠①:「スナップショットなのに書き込もうとする」
`dbOpenSnapshot` で開いたレコードセットに対して、`.Edit` や `.Update` を実行しようとすると、「操作は許可されていません」というエラーが発生します。
「見るためのデータ」なのか「直すためのデータ」なのか、設計の段階で明確に区別しましょう。
罠②:「Recordsetを閉じ忘れる(メモリリーク)」
これが最も恐ろしい罠です。
`Set rs = db.OpenRecordset(…)` を繰り返しながら、`rs.Close` を忘れると、Accessが確保しているメモリがどんどん膨れ上がり、最終的に「メモリ不足」でクラッシュします。
「開けたら、必ず閉じる」。これはエンジニアの絶対的な規律です。
罠③:「トランザクションの勘違い」
「ダイナセットを使えば、自動的にデータが安全に保存される」と思っていませんか?
ダイナセットはあくまで「データの窓口」です。複数のテーブルをまたぐ複雑な更新を行う場合は、`DBEngine.BeginTrans` を使ったトランザクション処理を組み合わせる必要があります。
(※トランザクションは「全成功か、全失敗か」を保証する仕組みです)
まとめ:次の一歩へ
Access VBAの世界では、「すべてを万能なもの(Dynaset)で済ませようとしないこと」が、プロへの第一歩です。
- 見るだけなら、Snapshotでスマートに。
- 直すなら、Dynasetで確実に。
この使い分けができるようになると、あなたの作るアプリは、ユーザーにとって「サクサク動いてストレスのない、最高の道具」に進化します。
「なぜこのコードを書くのか?」という理由を常に意識してみてください。その積み重ねが、あなたを本物のエンジニアへと導いてくれます。
さあ、次は実際にコードを書いて、その速度の違いを体感してみましょう!応援していますよ。
