ネットワークの壁を突破せよ:ADO BatchUpdateによる高速一括更新と競合解決の極意
業務アプリケーションの開発において、多くのエンジニアが陥る「初歩的な罠」がある。それは、「ループの中でSQLを発行する」という設計だ。
1,000件のデータを更新するために、1,000回 `UPDATE` 文をネットワーク越しにSQL Serverへ投げる。この設計は、一見するとシンプルで理解しやすい。しかし、これはプロフェッショナルなアーキテクトが絶対に選ばない「最悪の選択」だ。ネットワークの往復(Round-trip)によるレイテンシが積み重なり、ユーザーは「フリーズした」と感じ、データベースサーバーは無駄な通信オーバーヘッドに悲鳴を上げる。
本稿では、このボトルネックを根底から破壊するADOの`BatchUpdate`モードについて、そのアーキテクチャ的背景から、実戦で必ず直面する「更新競合」の解決策まで、極限の知見を伝授する。
1. なぜ「DAO」ではなく「ADO」なのか:アーキテクチャの境界線
まず、基礎的な設計思想を整理しておく。Access開発において、DAO(Data Access Objects)とADO(ActiveX Data Objects)の使い分けは、単なる好みの問題ではない。
- DAOは、Access(Jet/ACEエンジン)の内部構造に深く根ざした、ローカルファイル操作に最適化されたエンジンだ。
- ADOは、SQL ServerやOracleといった、エンタープライズ層のRDBMSへ接続するための、汎用的かつ強力なブリッジだ。
大量のデータをSQL Serverへ流し込む、あるいは同期させる場合、我々が手にすべき武器はADOである。そして、その真価を発揮させる鍵が、レコードセットをクライアントサイドのメモリ上に展開する`CursorLocation = adUseClient`の設定にある。
2. BatchUpdateのメカニズム:通信回数の劇的な削減
通常の `Update` メソッドは、呼び出されるたびにサーバーへ命令を飛ばす。対して `UpdateBatch` は、変更内容をクライアント側のメモリ(レコードセット)に一旦蓄積し、一括してサーバーへパケットを送り込む。
この挙動を実現するためには、以下の3つの条件が不可欠である。
1. `CursorLocation` を `adUseClient` に設定すること:サーバー側カーソルでは、クライアント側での変更の「保持」ができない。
2. `BatchSize` を適切に設定すること:一度に送るチャンクのサイズを制御する。
3. トランザクションの範囲を設計すること:一括更新は「全成功か全失敗か」の原則に従う必要がある。
3. 実践:堅牢な一括更新プロトコル
以下に、実務でそのまま使える、プロダクションレベルのコードパターンを示す。単なるコピペで終わらせるのではなく、なぜこの構造が必要なのかを理解してほしい。
Option Explicit
''' <summary>
''' SQL Serverに対して大量のレコードを一括更新する。
''' 更新競合が発生した場合は、エラーコレクションを走査してログを記録する。
''' </summary>
Public Sub ExecuteHighPerformanceBatchUpdate()
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim strConn As String
Dim i As Long
Dim errorCount As Long
' 1. 接続文字列の設定(環境に合わせて適切に管理すること)
strConn = "Provider=SQLNCLI11;Data Source=YOUR_SERVER;Initial Catalog=YOUR_DB;Integrated Security=SSPI;"
On Error GoTo ErrorHandler
Set cn = New ADODB.Connection
cn.ConnectionString = strConn
cn.Open
' 2. Recordsetの構築
' BatchUpdateを使用するためには、必ず adUseClient を指定する
Set rs = New ADODB.Recordset
rs.CursorLocation = adUseClient
' 更新対象のデータを取得(ここでは例として全件取得)
' ※実務ではWHERE句で対象を絞り込むこと
rs.Open "SELECT ID, Status, UpdateCount FROM TargetTable", cn, adOpenStatic, adLockOptimistic
' BatchSizeの設定(メモリ消費と速度のトレードオフ)
rs.BatchSize = 100
' 3. メモリ上でのデータ操作(ここまではネットワーク通信が発生しない)
If rs.EOF Then Exit Sub
Do Until rs.EOF
' 何らかのビジネスロジックに基づいた更新
rs!Status = "PROCESSED"
rs!UpdateCount = rs!UpdateCount + 1
' レコードの変更をメモリにマークする
rs.Update
rs.MoveNext
Loop
' 4. 変更内容の一括送信(ここで初めてネットワーク通信が発生する)
Debug.Print "BatchUpdateを開始します..."
rs.UpdateBatch
Debug.Print "BatchUpdateが正常に完了しました。"
CleanUp:
' リソースの解放
If Not rs Is Nothing Then
If rs.State = adStateOpen Then rs.Close
End If
If Not cn Is Nothing Then
If cn.State = adStateOpen Then cn.Close
End If
Set rs = Nothing
Set cn = Nothing
Exit Sub
ErrorHandler:
' 5. 競合解決(Optimistic Concurrency Errorのハンドリング)
' UpdateBatchが失敗した場合、エラーは rs.Errors コレクションに格納される
If Err.Number = -2147217887 Then ' ADODB_E_ERRORS_OCCURRED (競合が発生した際の代表的なエラー)
Debug.Print "警告: 更新競合が発生しました。個別のエラーを確認します。"
Dim errObj As ADODB.Error
errorCount = 0
For Each errObj In rs.Errors
errorCount = errorCount + 1
' ここで競合したレコードの特定や、ログ記録を行う
' errObj.Description には、どのレコードが、なぜ失敗したかの詳細が含まれる
Debug.Print "Error " & errorCount & ": " & errObj.Description
Next errObj
' 戦略的判断:
' 全件ロールバックするか、競合したものを除いて再試行するかを設計に基づいて決定する
MsgBox errorCount & " 件の更新競合が発生しました。詳細はログを確認してください。", vbCritical
Else
MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical
End If
Resume CleanUp
End Sub
4. アーキテクトの視点:競合解決(Concurrency Resolution)の設計
上記のコードで最も重要なのは、`ErrorHandler` 内での `rs.Errors` の扱いだ。
`BatchUpdate` を使用する場合、複数のレコードが同時に更新されていると、「楽観的並行性制御(Optimistic Concurrency Control)」に抵触する。これは、「自分がデータを読み取ってから、更新をかけるまでの間に、他の誰かがデータを書き換えた」場合に発生する。
設計者として、以下の3つの戦略のいずれかを事前に決めておく必要がある。
1. Fail Fast(即時停止): 1件でも競合があれば、全件をロールバックし、ユーザーに手動での再入力を促す。最も安全だが、ユーザー体験は低い。
2. Ignore & Log(無視と記録): 競合したレコードのみをスキップし、成功したものだけを確定させる。ログを詳細に残し、後で管理者がフォローする。
3. Last Win / Force Update(強制上書き): 競合を無視して、現在の値を強制的に書き込む。これはデータ整合性の観点から、極めて慎重な判断が求められる。
5. まとめ:堅牢なシステムへの道
Access VBAを単なる「小規模なツール作成の手段」と捉えるか、「エンタープライズ・データ連携のハブ」と捉えるか。その差は、こうした低レイヤの通信制御と、エラーに対する設計思想の深さに現れる。
- `adUseClient` を使いこなせ。
- `UpdateBatch` でネットワークの往復を最小化せよ。
- `Errors` コレクションを軽視せず、競合解決のロジックを設計に組み込め。
これらを遵守することで、君が作るツールは、単なる「動くコード」から、過酷な業務現場に耐えうる「信頼されるシステム」へと昇華する。
