【実務・中級編】DAOの「dbFailOnError」オプションを使いこなす:更新処理の安全性を高める隠れた切り札 – Access VBA解析バイブル

スポンサーリンク
スポンサーリンク

データの整合性を守る最後の砦:DAOの「dbFailOnError」を使いこなす設計思想

開発現場において、もっとも恐ろしいのは「エラーが出ること」ではない。
「エラーが起きているのに、エラーが出ずに処理が完了してしまうこと」だ。

もし君が、数千件の在庫データを更新するクエリを走らせ、そのうちの1件がバリデーションエラーで更新に失敗したとする。しかし、プログラムは平然と「処理完了」を返し、ユーザーは笑顔で画面を閉じる。その裏で、帳簿と実在庫の不一致が発生しているのだとしたら――。

これは、単なるプログラミングミスではない。「データの整合性に対する設計思想の欠如」である。

今回は、Access VBAにおけるDAO(Data Access Objects)操作において、この「サイレント・エラー(静かなる失敗)」を阻止するための極めて強力な武器、`dbFailOnError` オプションについて、その真価と正しい設計パターンを伝授する。

1. なぜ、デフォルトの `Execute` は「危険」なのか

DAOの `Database.Execute` メソッドを使用する際、多くの開発者は以下のようなコードを書いてしまう。

' --- 典型的な、しかし危険なコード ---
CurrentDb.Execute "UPDATE T_Inventory SET Stock = Stock - 1 WHERE ProductID = 101"

この書き方では、もし `ProductID = 101` が存在しない、あるいは型変換エラーが発生したとしても、VBAはエラーを投げず、そのまま次の行へ進んでしまう。

DAOのデフォルト挙動は「可能な限り実行し、失敗しても無視する」という、ビジネスロジックとしては極めて不誠実なものなのだ。プロフェッショナルとして、このような「不完全な成功」を許してはならない。

2. `dbFailOnError`:Fail-Fast(早期失敗)の原則

ここで登場するのが `dbFailOnError` オプションである。これを使用することで、DAOエンジンに対して「一つでも失敗があれば、即座にエラーを発生させ、処理を中断せよ」という命令を下すことができる。

設計思想:Fail-Fast

ソフトウェア設計における重要な原則の一つに「Fail-Fast(早期失敗)」がある。問題が発生した際、それを隠蔽せず、即座に検知してシステムを停止させることで、被害(データの汚染)を最小限に食い止める考え方だ。

`dbFailOnError` は、VBAの世界における Fail-Fast を実現するためのスイッチなのだ。

3. 実践:堅牢な更新処理のプロダクションコード

では、実際にどのように実装すべきか。単にオプションを付けるだけでなく、エラーハンドリングと組み合わせた「実戦で使える」テンプレートを示そう。

''' <summary>
''' 在庫更新処理を実行する。
''' dbFailOnError を使用し、不完全な更新を許さない設計。
''' </summary>
Public Sub UpdateInventorySafe(ByVal targetProductID As Long, ByVal changeAmount As Integer)
    Dim db As DAO.Database
    Dim strSql As String
    
    ' エラーハンドリングの開始
    On Error GoTo ErrorHandler
    
    Set db = CurrentDb
    
    ' 更新SQLの構築
    strSql = "UPDATE T_Inventory SET Stock = Stock + " & changeAmount & _
             " WHERE ProductID = " & targetProductID
    
    ' 【重要】dbFailOnError を指定することで、更新に失敗した瞬間に ErrorHandler へジャンプする
    ' これにより、一部のレコードだけが更新されるといった「中途半端な状態」を検知できる
    db.Execute strSql, dbFailOnError
    
    Debug.Print "更新成功: " & targetProductID
    
ExitSub:
    ' オブジェクトの解放(メモリリーク防止の基本)
    Set db = Nothing
    Exit Sub

ErrorHandler:
    ' エラー内容のログ出力やユーザーへの通知
    ' ここでは実務を想定し、詳細なエラー情報を出力する
    MsgBox "致命的なデータ更新エラーが発生しました。" & vbCrLf & _
           "エラー番号: " & Err.Number & vbCrLf & _
           "エラー内容: " & Err.Description, vbCritical, "システムエラー"
    
    ' プロダクション環境では、ここでログファイルへの書き出し等を行う
    Resume ExitSub
End Sub

4. 【極限の知見】 `dbFailOnError` と「トランザクション」の決定的な違い

ここで、中級者が陥りやすい致命的な誤解を解いておこう。
「`dbFailOnError` を使っていれば、トランザクション(Commit/Rollback)は不要である」という考えは、完全に間違いだ。

この二つの役割は、全く異なる。

機能 役割 目的
`dbFailOnError` エラーの検知 「失敗したこと」をプログラムに知らせる(検知器)
トランザクション 状態の復元 「失敗したとき」に、実行前の状態に戻す(復元装置)

なぜ両方必要なのか?

例えば、「Aテーブルの在庫を減らす」→「Bテーブルの売上を増やす」という2ステップの処理を考えてほしい。

1. `dbFailOnError` を付けて、Aテーブルの更新に成功。
2. Bテーブルの更新でエラーが発生。
3. `dbFailOnError` によりエラーは検知できるが、Aテーブルの更新は既に確定(コミット)されている。

結果として、在庫だけ減って売上が上がっていないという、最悪のデータ不整合が発生する。

「検知(dbFailOnError)」して、「復元(Transaction)」する。
この両輪が揃って初めて、業務システムとしての信頼性が担保されるのだ。

理想的なアーキテクチャ例

Public Sub ComplexBusinessProcess()
    Dim db As DAO.Database
    Dim ws As DAO.Workspace
    
    On Error GoTo ErrorHandler
    
    Set ws = DBEngine.Workspaces(0)
    Set db = CurrentDb
    
    ' --- トランザクション開始 ---
    ws.BeginTrans
    
    ' 1. 在庫更新 (dbFailOnError で失敗を検知)
    db.Execute "UPDATE T_Inventory SET Stock = Stock - 1 WHERE ID = 1", dbFailOnError
    
    ' 2. 売上記録 (dbFailOnError で失敗を検知)
    db.Execute "INSERT INTO T_Sales (ProductID, Qty) VALUES (1, 1)", dbFailOnError
    
    ' 全ての工程が成功した場合のみ確定
    ws.CommitTrans
    MsgBox "全ての処理が正常に完了しました。"

ExitSub:
    Set db = Nothing
    Set ws = Nothing
    Exit Sub

ErrorHandler:
    ' 失敗した場合、全ての操作を「開始前」の状態に巻き戻す
    If Not ws Is Nothing Then ws.Rollback
    
    MsgBox "プロセスが中断されました。全ての変更は取り消されました。" & vbCrLf & Err.Description, vbCritical
    Resume ExitSub
End Sub

5. まとめ:アーキテクトからの提言

君が作成するツールが、単なる「動くコード」なのか、それとも「信頼されるシステム」なのか。その境界線は、こうした細部へのこだわりにある。

1. `Execute` を使うときは、常に `dbFailOnError` をセットで考えること。
2. 単一のSQLなら `dbFailOnError` で十分だが、複数の更新を伴うなら必ず `Transaction` を併用すること。
3. エラーを「隠す」のではなく、「検知して、適切に処理(あるいはロールバック)する」設計を徹底すること。

「たぶん大丈夫だろう」という甘い設計は、将来の自分、あるいは同僚を必ず苦しめることになる。堅牢なコードを書くことは、プロフェッショナルとしての誠実さそのものなのだ。

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