Access VBAにおける「トランザクション×QueryDef」の深淵:整合性を担保する極限の制御術
Access開発において、`QueryDef`を駆使した動的SQL構築は基本中の基本だ。しかし、複数の更新処理が絡み合うとき、単に`db.Execute`を並べるだけの素人仕事で済ませていないか?
システムが肥大化し、数万件のレコードを操作する局面で「一部だけ更新が成功して一部が失敗する」という最悪の事態――いわゆるデータの不整合を許容することは、エンジニアとして死を意味する。
本稿では、`QueryDef`によるSQL生成と、DAOの`Workspace`を介したトランザクション制御を組み合わせ、堅牢なアーキテクチャを構築するための「極限の知見」を共有する。
—
1. トランザクション制御の真髄:`Workspace`オブジェクトの活用
Accessにおいてトランザクションを制御する際、`CurrentDb`を安易に使うのは推奨されない。なぜなら、`CurrentDb`は参照のたびに新しいインスタンスを生成する可能性があり、トランザクションのスコープが不安定になるからだ。
真のプロフェッショナルは、明示的に`Workspace`オブジェクトを定義し、そのスコープ内で処理を完結させる。
‘ 堅牢なトランザクション制御のテンプレート
Public Sub ExecuteTransactionalQuery()
Dim wrk As DAO.Workspace
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
‘ デフォルトワークスペースの取得
Set wrk = DBEngine(0)
Set db = CurrentDb
On Error GoTo ErrorHandler
‘ トランザクション開始
wrk.BeginTrans
‘ QueryDefの動的生成と実行
Set qdf = db.CreateQueryDef(“”, “UPDATE T_Target SET Status = ‘Processed’ WHERE ID = [TargetID]”)
qdf.Parameters(“TargetID”) = 101
qdf.Execute dbFailOnError ‘ dbFailOnErrorは必須。これを忘れるのは怠慢だ。
‘ 別の更新処理
qdf.SQL = “INSERT INTO T_Log (Action, Time) VALUES (‘Update’, Now())”
qdf.Execute dbFailOnError
‘ コミット
wrk.CommitTrans
GoTo Cleanup
ErrorHandler:
‘ ロールバックの徹底
If Not wrk Is Nothing Then wrk.Rollback
MsgBox “Critical Error: Transaction Rolled Back. ” & Err.Description, vbCritical
Cleanup:
‘ オブジェクトの明示的解放(メモリ管理の基本)
If Not qdf Is Nothing Then qdf.Close: Set qdf = Nothing
Set db = Nothing
Set wrk = Nothing
End Sub
—
2. なぜ `dbFailOnError` なのか?
`Execute`メソッドに`dbFailOnError`を指定しないコードは、地雷原を裸足で歩くのと同じだ。このフラグがないと、クエリ実行時に発生した違反(キー制約の重複、データ型の不整合など)をVBAが握りつぶし、成功したかのように次の行へ進んでしまう。
トランザクション下では、「エラーが発生した瞬間に例外を投げ、Catchブロックへ遷移させる」ことが絶対条件である。
—
3. パラメータクエリとメモリの最適化
`QueryDef`をループ内で動的に生成・破棄する場合、注意が必要だ。Accessの内部キャッシュが飽和すると、パフォーマンスは急激に低下する。
- 名前付きクエリの回避: 一時的な処理であれば、`CreateQueryDef(“”, SQL)`のように第1引数を空文字列にすることで、名前を持たない一時クエリとしてメモリに展開させる。これにより、データベースウィンドウを汚さず、処理終了後の解放も確実に行える。
- パラメータの型指定: `qdf.Parameters(“…”) = value`と書く際、暗黙の型変換に頼るな。データ量が増えるほど、型不一致によるインデックスの効かないフルスキャンが発生する。可能な限り`.Value`に明示的に型変換を行うこと。
—
4. レガシー環境におけるWindows APIとの連携(応用)
さらに高度な制御を行う場合、トランザクションの最中に長時間ロックを占有することを避ける必要がある。例えば、外部API連携を伴う複雑な更新処理において、DBがロックされている時間を最小化するために、あえて`LockFile`などのWindows APIを用いてプロセス間排他制御を行うこともある。
しかし、まずは「DAOのトランザクションを正しく閉じる」という基本を極めよ。リソースを解放しないコードは、やがてメモリリークを引き起こし、長期間稼働するシステムにおいて必ずクラッシュを招く。
—
5. 伝説的アーキテクトからの提言
コードを書くとき、常に「この処理が電源断やプロセス強制終了で中断されたらどうなるか?」を想像しろ。
- `Set db = Nothing` は単なる作法ではなく、DAOエンジンのクリーンアップを促す儀式である。
- `On Error GoTo` は回避策ではなく、堅牢なシステムを構築するための唯一の防波堤である。
Accessという枯れた環境であっても、設計思想を研ぎ澄ませば、エンタープライズに耐えうる堅牢なシステムは構築できる。技術は魔法ではない。論理の積み重ねである。
貴殿の書くコードが、次に触れるエンジニアにとっての「教科書」となることを期待している。健闘を祈る。
