接続の鎖を解き放て:ADO Disconnected Recordsetで実現する「究極のオフライン編集」アーキテクチャ
こんにちは。日々、コードと格闘しているあなたへ。
「マクロの記録」から一歩踏み出し、VBAのコードを自分で書き始めたあなたなら、一度はこんな壁にぶつかったことがあるはずです。
*「データを編集している最中に、ネットワークが切れたらどうしよう?」*
*「ユーザーが編集画面を開いたまま長時間放置して、他の人がそのレコードを更新できなくなったらどうしよう?」*
これらは、単なる「プログラムの書き方」の問題ではなく、「データのライフサイクルをどう設計するか」という、アーキテクチャ(設計思想)の問題です。
今日は、Access VBAの真髄の一つである「ADOのDisconnected Recordset(切断型レコードセット)」についてお話ししましょう。これを知れば、あなたの作るツールは「単なる入力フォーム」から「プロフェッショナルな業務システム」へと進化します。
ここをクリアすれば、Access VBAの基本はバッチリですよ。さあ、一緒に深淵を覗いてみましょう。
1. DAOとADO:その決定的な違いを知る
まず、整理しておきましょう。Accessにはデータを扱うための「道具」が主に2つあります。
| 特徴 | DAO (Data Access Objects) | ADO (ActiveX Data Objects) |
|---|---|---|
| 出自 | Accessのエンジン(Jet/ACE)専用 | Microsoftの汎用データアクセス規格 |
| 得意分野 | Access内部の高速な操作 | SQL Serverなどの外部DBとの連携 |
| 接続スタイル | 常時接続型 (常にDBと繋がっている) | 切断可能型 (メモリ上でデータを扱える) |
多くの初心者はDAOを使います。それはDAOがAccessにとって最も自然だからです。しかし、今回扱うDisconnected Recordset(切断型レコードセット)は、ADOにしかできない高等テクニックです。
接続型のイメージ(DAOなど)
「図書館の閲覧室で、本を机に広げて読んでいる状態」です。あなたが本を広げている間、他の人はその本を借りることができません(ロックがかかる)。
切断型のイメージ(ADO Disconnected)
「図書館で本を借りて(チェックアウトして)、自分の家でメモを取って、後でまとめて返却する状態」です。あなたが家で何をしていようと、図書館の席は空いています。
これが、「ネットワークの不安定さ」や「レコードのロック問題」を解決する鍵なのです。
2. Disconnected Recordsetの動作フロー
「切断型」の動きを、図解的にイメージしてみましょう。
【通常の接続型】
[App] <----(常に通信)----> [Database]
↑ ↑
└─ 編集中に通信が切れるとエラー! └─ レコードがロックされる
【切断型 (Disconnected)】
1. [App] --(接続)--> [Database] (データを吸い上げる)
2. [App] <--(切断)-- [Database] (DBとの接続を完全に切る!)
3. [App] (メモリ上のRecordsetで自由に編集)
4. [App] --(再接続)--> [Database] (変更分だけを流し込む)
このフローの最大の利点は、「DBとの通信時間を最小限に抑えられる」こと。そして、「ユーザーが編集している間、DBに負荷をかけない」ことです。
3. 実践:オフライン編集ツールの実装
では、実際に動くコードを見ていきましょう。
今回は、「テーブルからデータをメモリに読み込み、メモリ上で書き換えた後、一括でテーブルに反映する」という流れを実装します。
事前準備
Access内に `T_Sample` というテーブル(フィールド:`ID` (数値型), `TargetName` (テキスト型))があるものと仮定します。
実装コード
Option Explicit
' 参照設定: "Microsoft ActiveX Data Objects x.x Library" が必要です
' ※最新の 6.1 を推奨します
''' <summary>
''' データをメモリ上に展開する(切断型レコードセットの作成)
''' </summary>
Public Function GetDisconnectedRecordset() As ADODB.Recordset
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim strSql As String
' 1. 接続文字列の設定 (Access内部のテーブルに接続する場合)
' 本来はSQL Server等の外部DBに向けるのがこの手法の真価です
Dim strConn As String
strConn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & CurrentProject.FullName & ";"
Set cn = New ADODB.Connection
cn.Open strConn
' 2. レコードセットの生成
Set rs = New ADODB.Recordset
strSql = "SELECT ID, TargetName FROM T_Sample"
' 【重要】Disconnectedを実現するための設定
' CursorType: adUseClient (クライアントサイドカーソルを使用)
' LockType: adLockOptimistic (楽観的ロック)
rs.Open strSql, cn, adUseClient, adLockOptimistic
' 3. 接続を切り離す
' これにより、rsはデータを持ったまま、cnとの関係を解消します
Set rs.ActiveConnection = Nothing
cn.Close
Set cn = Nothing
' メモリ上のレコードセットを返す
Set GetDisconnectedRecordset = rs
End Function
''' <summary>
''' メモリ上の変更をDBへ一括同期する
''' </summary>
Public Sub SyncRecordsetToDB(rs As ADODB.Recordset)
Dim cn As ADODB.Connection
Dim strConn As String
If rs Is Nothing Then Exit Sub
' 1. 再接続
strConn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & CurrentProject.FullName & ";"
Set cn = New ADODB.Connection
cn.Open strConn
' 2. レコードセットをDBに再接続させる
rs.ActiveConnection = cn
On Error GoTo Err_Handler
' トランザクション開始(一括処理の整合性を守るため)
cn.BeginTrans
' 3. 更新の実行
' 内部的に、変更があったレコードのみを特定してUpdateをかけます
rs.UpdateBatch
' 4. コミット
cn.CommitTrans
MsgBox "同期が完了しました。", vbInformation
Exit_Handler:
If Not cn Is Nothing Then
If cn.State = adStateOpen Then cn.Close
End If
Set cn = Nothing
Exit Sub
Err_Handler:
' エラーが起きたらロールバック(すべてをなかったことにする)
cn.RollbackTrans
MsgBox "エラーが発生したため、同期を中止しました: " & Err.Description, vbCritical
Resume Exit_Handler
End Sub
このコードの「プロの視点」解説
1. `adUseClient` の魔法:
これが最も重要です。通常、レコードセットはサーバー上のデータを見に行きますが、`adUseClient` を指定することで、データを手元のメモリ(クライアント側)にコピーして持ってくるよう命令しています。これがないと「切断」はできません。
2. `Set rs.ActiveConnection = Nothing`:
これが「物理的な切断」の瞬間です。ここでDBとのパイプラインを断ち切ることで、ネットワークトラブルの影響を完全に遮断します。
3. `UpdateBatch` と `BeginTrans`:
バラバラに `Update` を送るのではなく、変更をまとめてドカンと送るのが `UpdateBatch` です。そして、万が一途中でエラーが起きた場合に「中途半端に更新されたデータ」が残らないよう、`BeginTrans`(トランザクション)で守りを固めています。
4. 陥りやすい罠(アーキテクトからの助言)
この高度な手法を使うとき、初心者が必ずと言っていいほどハマるポイントが2つあります。
① 楽観的ロックの衝突 (Optimistic Concurrency)
切断型は「自分が編集している間、他の人が更新しても構わない」というスタンスです。しかし、いざ同期(Update)しようとしたとき、「あれ?さっき借りた時と、DBの中身が変わってるぞ?」という状況が起こり得ます。
これを「衝突」と呼びます。実務では、衝突が起きた際に「どちらのデータを優先するか」のロジック(エラーハンドリング)を組み込む必要があります。
② メモリ消費量
「何でもかんでも切断型で」は危険です。数百万件のデータを `adUseClient` で読み込むと、あなたのPCのメモリが悲鳴を上げ、Accessがクラッシュします。
「必要な分だけ、WHERE句で絞り込んでから読み込む」。これは鉄則です。
最後に
Disconnected Recordsetは、一見すると複雑です。しかし、この「データをメモリに隔離して、安全に、かつ効率的に扱う」という考え方は、Webアプリケーションや大規模なエンタープライズシステムでも共通する、極めて重要な概念です。
「ただ動くコード」を書く段階から、「堅牢で効率的なシステム」を設計する段階へ。
あなたは今、その境界線を越えようとしています。
一歩ずつ、着実に進んでいきましょう。応援していますよ。
