【入門編】ADOのDisconnected Recordsetで実現する:オフライン環境でのデータ編集ツール – Access VBA解析バイブル

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

接続の鎖を解き放て: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アプリケーションや大規模なエンタープライズシステムでも共通する、極めて重要な概念です。

「ただ動くコード」を書く段階から、「堅牢で効率的なシステム」を設計する段階へ。
あなたは今、その境界線を越えようとしています。

一歩ずつ、着実に進んでいきましょう。応援していますよ。

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