Access VBAの「整合性」を殺すな:DAOトランザクションによる堅牢な更新処理の極意
業務自動化の現場でよく見る「死のパターン」がある。複数のテーブルを更新する際、エラーハンドリングを怠り、途中で処理が止まってデータが不整合を起こすケースだ。
「顧客テーブルは更新されたが、受注テーブルは空のまま」
こんな状態になれば、その後の集計や分析はすべてゴミになる。Access VBAで真にプロフェッショナルなツールを作るなら、「全か無か(All-or-Nothing)」の原則、すなわちトランザクション管理は避けて通れない。
今日は、DAO.Recordsetを用いたトランザクション実装の「正解」を伝授する。
—
なぜ `DBEngine.BeginTrans` なのか
Accessの標準機能である `CurrentDb` を使っている諸君、トランザクションの開始点はどこにあるか知っているか?
実は、トランザクション管理は `CurrentDb` ではなく、`DBEngine` オブジェクトに対して行う。`CurrentDb` は呼び出すたびに新しいインスタンスを生成する可能性があるため、トランザクションのコンテキストを維持するなら、明示的に `DBEngine` を参照するのがエンジニアとしての作法だ。
堅牢なトランザクション実装のテンプレート
以下に、実戦投入レベルのテンプレートを示す。このコードの肝は、エラー発生時に確実に `Rollback` を実行し、トランザクションをクローズさせることにある。
Public Sub SafeUpdateProcess()
Dim ws As DAO.Workspace
Dim db As DAO.Database
Dim rsCustomer As DAO.Recordset
Dim rsOrder As DAO.Recordset
‘ 1. デフォルトワークスペースを取得
Set ws = DBEngine.Workspaces(0)
Set db = CurrentDb
‘ トランザクション開始
ws.BeginTrans
On Error GoTo ErrorHandler
‘ — 処理開始 —
Set rsCustomer = db.OpenRecordset(“T_Customer”, dbOpenDynaset)
rsCustomer.AddNew
rsCustomer!CustomerName = “伝説のエンジニア”
rsCustomer.Update
Set rsOrder = db.OpenRecordset(“T_Order”, dbOpenDynaset)
rsOrder.AddNew
rsOrder!OrderDate = Date
rsOrder.Update
‘ — 処理終了 —
‘ すべて成功したらコミット
ws.CommitTrans
MsgBox “更新が正常に完了しました。”, vbInformation
CleanExit:
‘ オブジェクトの解放は忘れずに
If Not rsOrder Is Nothing Then rsOrder.Close: Set rsOrder = Nothing
If Not rsCustomer Is Nothing Then rsCustomer.Close: Set rsCustomer = Nothing
Exit Sub
ErrorHandler:
‘ エラー発生時はロールバック
ws.Rollback
MsgBox “致命的なエラーが発生したため、処理をロールバックしました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“内容: ” & Err.Description, vbCritical
Resume CleanExit
End Sub
この設計の「プロのこだわり」
1. `ws.BeginTrans` の配置:
処理の直前に置くこと。トランザクションの範囲を可能な限り狭くするのが、マルチユーザー環境におけるパフォーマンス維持の鉄則だ。範囲が広すぎると、その間テーブル全体がロックされ、他のユーザーが作業できなくなる。
2. `On Error GoTo ErrorHandler` の絶対性:
VBAにおいてエラーを無視するのは罪だ。必ず `Rollback` を経由させる設計にせよ。さもなくば、ロックされたままのレコードがDBを占拠し、最悪の場合、.accdbファイルそのものが破損するリスクを孕む。
3. オブジェクトのクリーンアップ:
`Set … = Nothing` を `CleanExit` ラベルに置くことで、正常系・異常系問わずメモリ上のリソースを確実に解放する。これが長期間稼働するシステムの安定性を支える。
さらなる高みへ:設計の注意点
- GUI更新との共存:
もしフォーム上で入力された値を更新する場合、`Me.Dirty = False` をトランザクション開始前に呼び出し、フォーム上の未保存データを強制的に確定させる必要がある。これを行わないと、フォームのバリデーションとVBAの更新ロジックが衝突し、不整合を引き起こす。
- バックエンド分離の重要性:
データは必ず分離(フロント/バック)しろ。トランザクション処理は、リンクテーブル経由でも機能するが、ネットワークの安定性がパフォーマンスに直結する。LAN環境であれば問題ないが、クラウドストレージ上のファイルでこれをやろうとすれば、高確率でタイムアウトが起きる。それはVBAの責任ではない。
最後に:自動化は「責任」である
君たちが書くコードは、単なるスクリプトではない。エンドユーザーの業務データを預かる「番人」だ。エラーハンドリングを「面倒だ」と感じるなら、それはエンジニアとして成熟していない証拠だ。
今日紹介したこのテンプレートを標準装備にせよ。それだけで、君のツールは明日から格段に堅牢になる。実装に迷ったら、またここへ戻ってくるといい。常に最強のコードを用意して待っている。
