【入門編】DAOの「dbFailOnError」オプション:更新処理の安全性を高める隠れた切り札 – Access VBA解析バイブル

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

【Access VBAの極意】「なぜかデータが更新されない」を防ぐ魔法のオプション:`dbFailOnError`を使いこなそう

こんにちは。Access VBAの世界へようこそ。
マクロの記録から一歩踏み出し、自分でコードを書き始めたあなた。素晴らしい挑戦です!

コードが動くようになると、次は「もっと確実に、もっと安全に動かしたい」と思うはずです。実は、プロの現場で最も恐れられているのは「エラーが出て止まること」ではなく、「エラーが起きているのに、エラーにならずに処理が進んでしまうこと」なのです。

今日は、そんな「サイレントな失敗(静かなるデータ破壊)」を防ぎ、あなたのプログラムに鉄壁の守りを与える、DAOの隠れた切り札`dbFailOnError`についてお話ししましょう。

ここをクリアすれば、あなたのVBAスキルは「ただ動くコード」から「プロの品質のコード」へと一気に昇華しますよ。

1. あなたを襲う「サイレント・エラー」の恐怖

まず、多くの初学者が陥る「罠」をイメージしてみてください。

例えば、在庫管理システムを作っているとしましょう。「売上データを作る」と同時に「在庫数を減らす」という2つの処理をSQL(Executeメソッド)で行うコードを書いたとします。

' ⚠️ 危険な例:これではデータが矛盾する可能性がある
Dim strSQL1 As String
Dim strSQL2 As String

strSQL1 = "INSERT INTO 売上テーブル ..."
strSQL2 = "UPDATE 在庫テーブル SET 在庫数 = 在庫数 - 1 WHERE ..."

' 1つ目の処理を実行
CurrentDb.Execute strSQL1

' 2つ目の処理を実行(もしここでエラーが起きても...?)
CurrentDb.Execute strSQL2

ここで、もし「在庫テーブル」の制約(例えば在庫数が0以下になってはいけない、など)に引っかかり、`strSQL2`が失敗したとしたらどうなるでしょうか?

デフォルトの設定では、VBAは何も言わずに次の行へ進んでしまいます。

「売上データはあるのに、在庫が減っていない」という、会計上もっともあってはならないデータの矛盾(不整合)が発生してしまうのです。これが、プログラミングにおける「サイレント・エラー」の恐ろしさです。

2. 救世主 `dbFailOnError` とは何か?

この問題を解決するために用意されているのが、`Execute` メソッドの第2引数に指定できるオプション、`dbFailOnError` です。

通常、`CurrentDb.Execute strSQL` と書くと、DAO(Data Access Objects)は「エラーが起きても、とりあえずスルーしてね」というスタンスを取ります。

しかし、これを次のように書き換えるだけで、挙動が劇的に変わります。

' ✅ 安全な例:エラーを検知できるようにする
CurrentDb.Execute strSQL, dbFailOnError

何が変わるのか?(図解イメージ)

設定 エラー発生時の挙動 プログラムへの影響
なし (デフォルト) エラーを無視して、何事もなかったかのように次へ進む 【危険】 データが矛盾したまま処理が続く
`dbFailOnError` あり 即座にVBAのエラー(例外)を発生させる 【安全】 エラーハンドリング(Err.Number)へジャンプできる

`dbFailOnError` を指定すると、もしSQLの実行に失敗(主キーの重複、制約違反、型違いなど)した場合、Accessは「おい、失敗したぞ!」と叫びながら、プログラムの実行を中断し、エラーハンドリング(`On Error GoTo`)へと処理を飛ばしてくれます。

3. 真の最強コンボ:`dbFailOnError` × トランザクション

`dbFailOnError` は単体でも強力ですが、真価を発揮するのは「トランザクション処理」と組み合わせた時です。

トランザクションとは、「複数の処理をひとまとめにして、『全部成功するか』それとも『全部なかったことにするか』のどちらかにする」仕組みのことです。

これを「銀行の振込」で考えてみましょう。
1. Aさんの口座から1万円引く
2. Bさんの口座に1万円足す

もし「1」が成功して「2」が失敗したら、1万円が消えてしまいますよね?
「全部成功するか、失敗したら全部元に戻す(ロールバック)」というのが、プロの設計です。

実践的なテンプレートコード

現場でそのまま使える、もっとも堅牢なコードの型を紹介します。これをコピペして、自分のプロジェクトのベースにしてください。

Option Compare Database
Option Explicit

''' <summary>
''' 在庫を減らしつつ売上を記録する、安全な処理の例
''' </summary>
Public Sub SafeUpdateProcess()
    Dim db As DAO.Database
    Dim strSQLSales As String
    Dim strSQLStock As String
    
    ' エラーが発生したら「ErrorHandler」ラベルへジャンプする設定
    On Error GoTo ErrorHandler
    
    Set db = CurrentDb
    
    ' SQL文の準備
    strSQLSales = "INSERT INTO 売上テーブル (商品ID, 数量) VALUES (1, 10);"
    strSQLStock = "UPDATE 在庫テーブル SET 在庫数 = 在庫数 - 10 WHERE 商品ID = 1;"

    ' --- ここからトランザクション開始 ---
    ' これ以降の処理を「ひとまとめ」にする
    DBEngine.BeginTrans
    Debug.Print "トランザクションを開始しました。"

    ' 1. 売上データの追加
    ' dbFailOnError を指定することで、失敗時に即座に ErrorHandler へ飛ぶ
    db.Execute strSQLSales, dbFailOnError
    Debug.Print "売上データの追加に成功。"

    ' 2. 在庫データの更新
    ' もしここで在庫不足などのエラーが起きれば、即座に ErrorHandler へ飛ぶ
    db.Execute strSQLStock, dbFailOnError
    Debug.Print "在庫データの更新に成功。"

    ' --- すべての処理が正常に終わったら確定させる ---
    DBEngine.CommitTrans
    Debug.Print "すべての処理を確定(コミット)しました。"
    
    MsgBox "処理が正常に完了しました。", vbInformation

ExitSub:
    ' 後片付け(オブジェクトの解放)
    Set db = Nothing
    Exit Sub

ErrorHandler:
    ' --- エラーが発生した場合の処理 ---
    ' 1. トランザクションを「なかったこと」にする(ロールバック)
    ' これにより、途中で失敗したSQLの実行結果もすべて取り消される
    DBEngine.Rollback
    Debug.Print "エラーが発生したため、すべての処理を取り消しました(ロールバック)。"
    
    ' 2. エラー内容をユーザーに通知
    MsgBox "エラーが発生しました。処理を中断し、データを元に戻しました。" & vbCrLf & _
           "エラー番号: " & Err.Number & vbCrLf & _
           "エラー内容: " & Err.Description, vbCritical, "処理失敗"
    
    Resume ExitSub
End Sub

4. まとめ:エンジニアとしての心得

今日学んだことを振り返りましょう。

1. `Execute` メソッドはデフォルトでは「黙って失敗」する。
2. `dbFailOnError` を付けることで、失敗を「エラー」として検知できるようになる。
3. `BeginTrans`(開始)、`CommitTrans`(確定)、`Rollback`(取消)を組み合わせることで、データの不整合を完璧に防げる。

「動けばいい」という考え方は、開発段階では楽かもしれません。しかし、ユーザーが実際に使い始めたとき、データが壊れていたら、あなたの信頼は一瞬で失われてしまいます。

「失敗したときに、どうやって安全に止まるか」

これこそが、初級者を脱却し、プロのエンジニアへと進むための重要なマインドセットです。
`dbFailOnError` を使いこなして、誰からも信頼される、堅牢なシステムを作り上げてくださいね。

応援していますよ!

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