【実務・中級編】ADOのRecordsetを切断(Disconnected)してメモリ上で編集するテクニック – Access VBA解析バイブル

スポンサーリンク

【Access VBA極限知見】ADO切断型レコードセット(Disconnected Recordset)で実現する、爆速かつ安全なトランザクション設計

開発現場でこんな絶望を味わったことはないか?

  • 「複数ユーザーが同時にAccessのバックエンド(SQL Serverなど)にアクセスし、テーブルをロックしすぎて業務が止まった」
  • 「ネットワークの微小な瞬断で、リモートDBとのコネクションが切れて長時間の処理がロールバックされた」
  • 「フォームでの入力値検証(バリデーション)のたびにサーバーとラウンドトリップが発生し、画面がカクつく」

Access VBAにおけるデータベースアクセスといえばDAOがデフォルトだが、エンタープライズなシステム連携や、リモートDB(SQL Server, PostgreSQL等)をバックエンドに据えた高負荷な環境において、DAOの常時接続モデルは時として「百害あって一利なし」のボトルネックと化す。

今回は、ADO(ActiveX Data Objects)の真髄であり、プロだけが知る秘技「切断型レコードセット(Disconnected Recordset)」のアーキテクチャと、現場で即座に使えるプロダクションコードを授けよう。

サーバーとの接続を意図的に切り離し、クライアント(ローカルメモリ)上でデータを自在に料理し、最後に一括で同期(バッチ更新)させる。この手法をマスターすれば、あなたの作るAccessアプリのパフォーマンスと堅牢性は次元が違うレベルへと到達する。

—

1. なぜ「切断型(Disconnected)」なのか? アーキテクチャの真実

一般的なデータベース処理は、「接続(Connect) ➔ 取得(Open) ➔ 操作(Edit/Update) ➔ 切断(Close)」のライフサイクルを短く繰り返す。しかし、レコードの取得からユーザーの編集完了まで接続を維持し続けると、以下の弊害が生じる。

1. コネクションプールの枯渇とライセンス圧迫
2. 長時間の行ロック(Lock)による他ユーザーのブロック
3. ネットワーク障害に対する脆弱性

切断型レコードセットのライフサイクル

切断型レコードセットでは、データを取得した瞬間にあえてConnectionオブジェクトを破棄(Nothingに)する。

[SQL Server / Remote DB]
│
▼ (一時的に接続:データ取得)
[ADO Connection] ──(取得完了と同時に破棄!)
│
▼
[Disconnected Recordset] ──★メモリ上で自由自在に編集・追加・削除
│
▼ (再接続:一括同期)
[BatchUpdate] ➔ [SQL Serverへ反映]

データは完全にローカルのメモリ(RAM)上に引き剥がされているため、サーバー側のリソースを一切消費しない。ユーザーがどれだけ時間をかけてデータを改変しようとも、ネットワークがどう揺らいだとしても、サーバー側には何の影響も及ぼさないのだ。

—

2. 実装上のクリティカル・ポイント(罠の回避)

Disconnected Recordsetを実装するにあたり、アマチュアが必ず踏む地雷が2つある。

① カーソル場所(CursorLocation)は必ず `adUseClient` にせよ

デフォルトの `adUseServer` のままでは、コネクションを切断した瞬間にレコードセットも無効化される。クライアント側でデータを保持・操作するためには、必ずカーソルをクライアント側に置く必要がある。

② ロックタイプ(LockType)は `adLockBatchOptimistic` にせよ

後から一括で変更を書き戻す(Batch Update)するためには、オプティミスティック(楽観的)なバッチロックを指定しなければならない。

—

3. 【プロダクションコード】コピペで使える堅牢な実装例

以下のコードは、リモートのSQL Server等からデータを切断状態で取得し、メモリ上でデータを加工したのち、一括でデータベースへ同期(コミット)する完全版のプロシージャだ。

エラーハンドリング、オブジェクトの確実な解放(メモリリーク防止)、トランザクションの整合性を担保した、現場の最前線で使える品質となっている。

Option Compare Database
Option Explicit

‘ ==============================================================================
‘ 概要: ADO切断型レコードセットを使用して、サーバー接続を維持せずに
‘ メモリ上でデータを編集し、一括でデータベースに同期するサンプル
‘ ==============================================================================
sub ExecuteDisconnectedRecordsetSample()

Dim conn As Object ‘ ADODB.Connection
Dim rs As Object ‘ ADODB.Recordset
Dim connStr As String
Dim sql As String

‘ 接続文字列の定義(環境に合わせて書き換えてください)
‘ ※SQL Server認証の例
connStr = “Provider=SQLOLEDB;Server=192.168.1.50;Database=EnterpriseDB;” & _
“Uid=AppUser;Pwd=StrongPassword123;”

Set conn = CreateObject(“ADODB.Connection”)
Set rs = CreateObject(“ADODB.Recordset”)

On Error GoTo ErrorHandler

‘————————————————————————–
‘ 1. 接続と切断型設定の適用
‘————————————————————————–
conn.CommandTimeout = 30
conn.Open connStr

‘ 【超重要】カーソルをクライアント側に置くことで切断を可能にする
rs.CursorLocation = 3 ‘ adUseClient

sql = “SELECT EmployeeID, FirstName, LastName, Salary, UpdatedDate FROM M_Employees WHERE DepartmentID = 10”

‘ 【超重要】ロックタイプをバッチ更新用に設定
‘ adOpenStatic (3), adLockBatchOptimistic (4)
rs.Open sql, conn, 3, 4

‘————————————————————————–
‘ 2. ★ここで接続を完全に切断する!(コネクションの解放)
‘————————————————————————–
Set rs.ActiveConnection = Nothing
conn.Close
Set conn = Nothing ‘ サーバーリソースを即座に解放

‘ ————————————————————————-
‘ 3. メモリ上(切断状態)でのデータ操作
‘ サーバーに負荷をかけず、ここからはローカルRAM上の処理となる
‘ ————————————————————————-
Debug.Print “— 切断状態でのメモリ編集開始 —”

If Not (rs.EOF and rs.BOF) Then
rs.MoveFirst
Do Until rs.EOF
‘ 例:給与を一律5%アップさせる(ビジネスロジックの適用)
rs.Fields(“Salary”).Value = rs.Fields(“Salary”).Value 1.05
rs.Fields(“UpdatedDate”).Value = Now

Debug.Print “社員ID: ” & rs.Fields(“EmployeeID”).Value & ” の給与を更新しました。”

rs.MoveNext
Loop
End If

‘ 例:新規レコードの追加もメモリ上で完結する
rs.AddNew
rs.Fields(“EmployeeID”).Value = 9999
rs.Fields(“FirstName”).Value = “Test”
rs.Fields(“LastName”).Value = “User”
rs.Fields(“Salary”).Value = 300000
rs.Fields(“UpdatedDate”).Value = Now
rs.Update

Debug.Print “— メモリ編集完了。データベースへ再接続して一括同期します —”

‘————————————————————————–
‘ 4. 再接続と一括同期(Batch Update)
‘————————————————————————–
Set conn = CreateObject(“ADODB.Connection”)
conn.Open connStr

‘ トランザクションを開始し、一連の更新の原子性を担保する
conn.BeginTrans

‘ コネクションを再設定
Set rs.ActiveConnection = conn

‘ 【核心】メモリ上の変更をデータベースに一括書き込み
rs.UpdateBatch 1 ‘ adAffectAll (すべての変更行を対象)

‘ コミット
conn.CommitTrans

MsgBox “データの切断編集および一括同期が正常に完了しました。”, vbInformation, “成功”
GoTo Cleanup

ErrorHandler:
‘ エラー発生時のロールバック処理
If Not conn is Nothing Then
If conn.State = 1 Then conn.RollbackTrans
End If

MsgBox “エラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”

Cleanup:
‘ 徹底的なオブジェクトの破棄(メモリリークの根絶)
On Error Resume Next
If Not rs is Nothing Then
If rs.State = 1 Then rs.Close
Set rs = Nothing
End If
If Not conn is Nothing Then
If conn.State = 1 Then conn.Close
Set conn = Nothing
End If
On Error GoTo 0
End Sub

—

4. チーフアーキテクトからの実務アドバイス

このテクニックを現場に導入する際、以下の設計思想を忘れないでほしい。

  • 競合解決(Conflict Resolution)の考慮

`UpdateBatch` を実行する際、もし他のユーザーが同じレコードを先に更新していた場合、楽観的排他制御によりエラーが発生する。大規模なシステムでは、エラー時にどのフィールドでコンフリクトが起きたかを判定するロジック(`rs.Resync` 等の活用)を付加する必要がある。小規模〜中規模の業務ツールであれば、エラー時に「最新データが他者によって更新されています。やり直してください」とロールバックする設計で十分実用に耐える。

  • メモリ消費量の見積もり

数万件・数十万件レベルの巨大なレコードセットをすべてクライアント側のメモリ(Accessのプロセス空間)に載せると、クライアントPCのメモリを圧迫し、最悪の場合クラッシュする。切断型レコードセットが真価を発揮するのは、「数千件程度の中小規模マスタやトランザクションデータを、画面やフォームで高速に一括処理したい時」である。

DAOの限界にぶつかった時、あるいはパフォーマンスの壁に阻まれた時、この「ADO切断型レコードセット」というカードを切れるかどうかが、プロのエンジニアとアマチュアを分かつ境界線となる。

現場のシステムを次のステージへ引き上げるために、ぜひこのアーキテクチャを組み込んでみてほしい。

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