Access VBAを掌握せよ:DAOとADOの「Update」に見る、データ不整合との決別
Access開発の現場で、なぜか「データが保存されない」「排他制御で落ちる」というトラブルが絶えないのか。それは、DAOとADOがどのようにデータをメモリ(バッファ)から物理層へ流し込んでいるか、その「見えない時間軸」を意識していないからだ。
今日は、中級者から一歩先へ進むために、DAOの`Update`とADOの`Update`、それぞれのメモリ戦略と「真のコミット」の正体を解剖する。
—
1. DAOとADOの「Update」の決定的な違い
まず認識を改めるべきは、`Update`メソッド=「物理的な保存完了」ではないという事実だ。
- DAO (Data Access Objects): Accessのネイティブエンジン(ACE/Jet)との親和性が極めて高い。DAOの`Update`は、バッファをエンジン内のキャッシュに書き込む。トランザクションを使用しない限り、各レコード単位で即座に書き込みが試行されるが、ネットワーク越しや大規模な更新では暗黙的なキャッシュ遅延が発生する。
- ADO (ActiveX Data Objects): 接続指向のアーキテクチャ。`Update`はあくまで「レコードセットのバッファを更新」するだけだ。接続先がSQL Serverなら、`UpdateBatch`や`CommitTrans`を発行するまで、物理的なパケットは飛ばないことも多い。
「保存したはずなのに反映されていない」というバグの正体は、この「アプリケーション側のメモリ」と「DB側の物理データ」の乖離にある。
—
2. 堅牢な設計:トランザクションと強制フラッシュの流儀
データ欠損を防ぐ唯一の手段は、「書き込みを自分の制御下に置くこと」だ。以下の設計パターンを徹底せよ。
1. DAOなら「トランザクション境界」を明示する: 複数テーブルにまたがる更新は、必ず`BeginTrans` / `CommitTrans`で囲む。
2. ADOなら「即時更新モード」を理解する: サーバーサイドカーソルを使うのか、クライアントサイドか。デフォルトのバッファリングに頼らず、必要に応じて`Connection.Execute`で直接叩く判断も重要だ。
—
3. 実践:保守性を担保するプロダクションコード例
現場で「とりあえず動く」ではなく「落ちない」コードを書くためのテンプレートを提示する。DAOにおける安全な更新パターンだ。
‘ 【DAOトランザクション制御のベストプラクティス】
Public Sub SafeUpdateRecord(strID As String, strValue As String)
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim ws As DAO.Workspace
Set ws = DBEngine(0)
Set db = CurrentDb
‘ トランザクション開始:ここからが不可分な領域
ws.BeginTrans
On Error GoTo Err_Handler
Set rs = db.OpenRecordset(“SELECT FROM T_Master WHERE ID = ‘” & strID & “‘”, dbOpenDynaset)
If Not rs.EOF Then
rs.Edit
rs!Value = strValue
rs.Update ‘ この時点ではまだキャッシュレベル
End If
‘ 物理的なコミットを確定させる
ws.CommitTrans
rs.Close
Exit Sub
Err_Handler:
‘ 何かあれば全てロールバック。不整合なデータは残さない
ws.Rollback
MsgBox “更新中にエラーが発生しました。変更は破棄されました。” & vbCrLf & Err.Description, vbCritical
End Sub
このコードのポイント
- Workspaceの明示: `DBEngine(0)`を明示的に扱うことで、予期せぬロックやセッションの混在を防ぐ。
- エラーハンドリングの徹底: `Rollback`を忘れるコードは、未定義のデータ汚染を招く。エラーが起きたら「なかったことにする」のが基本原則だ。
—
4. プロの視点:なぜ「Flush」が必要なのか
大容量のCSV取り込みや一括更新処理を行う際、Accessはメモリを食いつぶし、ディスクI/Oのボトルネックに直面する。このとき、単に`Update`を繰り返すのではなく、以下のように処理を分断せよ。
- 定期的コミット: 10,000件を一気に更新せず、1,000件ごとに`CommitTrans`を挟み、`ws.BeginTrans`で再開する。これにより、トランザクションログの肥大化と、異常終了時の「全損」リスクを回避できる。
- キャッシュのクリア: DAOの`DBEngine.Idle`メソッドを活用せよ。これはメモリ上の不要なオブジェクトを解放し、ペンディング状態の書き込みを強制的に実行させる「掃除」のコマンドだ。
‘ 大量処理のループ内で適宜呼び出す
If i Mod 1000 = 0 Then
ws.CommitTrans
ws.BeginTrans
DBEngine.Idle dbFreeLocks ‘ ロック解放とキャッシュの整理
End If
—
結びに:エンジニアとしての矜持
VBAを「とりあえず動くスクリプト」と呼ぶか、「堅牢な業務基盤」と呼ぶかは、この「書き込みのタイミングを制御できているか」という一点にかかっている。
APIやモダンな言語を学ぶのも重要だが、まずは目の前のAccessという巨大なエンジンが、背後でどうやってデータを捌いているのかを想像してほしい。その想像力こそが、バグを未然に防ぐ最強の武器になる。
次にコードを書くとき、その`Update`が「どこで」「いつ」物理的に反映されるか、一瞬だけ考えてみてくれ。君の書くコードは、必ずより洗練されたものになるはずだ。
