【入門編】ADOの「Errorsコレクション」を駆使した、詳細なDBエラーログの自動生成ツール – Access VBA解析バイブル

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

表面的なエラーに惑わされないために。ADOの「Errorsコレクション」でSQL Serverの真実を掴み取る

こんにちは。日々、業務の自動化やシステムの最適化に励んでいる皆さん、お疲れ様です。

Access VBAを使い始めると、誰もが一度は「壁」にぶつかります。それは、「エラーが出たけれど、なぜエラーになったのかさっぱり分からない」という問題です。

Accessの画面に「実行時エラー ‘3022’: 重複する値があります」とだけ表示されて、プログラムが止まってしまう。あるいは、SQL Serverにデータを送ったはずなのに、なぜか反映されていない。

「マクロの記録」や、ネットで見つけたコードをそのまま貼り付けて動かす段階を卒業し、「システムがなぜ失敗したのか」という真実を、プログラム自身に語らせる。 これができるようになると、あなたはもう「ただのユーザー」ではなく、「エンジニア」の入り口に立っています。

今日は、プロが現場で必ずと言っていいほど活用している、ADO(ActiveX Data Objects)の「Errorsコレクション」を使った、極めて実戦的なエラーログ生成術を伝授しましょう。

1. なぜ「Err.Description」だけでは足りないのか?

VBAでエラー処理といえば、`On Error GoTo …` と書き、`Err.Number` や `Err.Description` を見るのが一般的ですよね。

しかし、ここで一つ、アーキテクチャの重要な違いを知っておいてください。

DAOとADOの違いを理解する

  • DAO (Data Access Objects): 主にAccess内部のテーブルを操作するための、いわば「家の中の管理人」です。動きが軽く、Accessの設計思想に最適化されています。
  • ADO (ActiveX Data Objects): SQL Serverなどの「外部の王国」と通信するための「外交官」です。ネットワーク越しに遠くのサーバーとやり取りをします。

ここが運命の分かれ道です。
あなたがSQL Serverに対して「このデータを保存して!」と依頼したとき、もしサーバー側で「制約違反(データの重複など)」が起きたとします。

このとき、Access側(VBA側)に返ってくる `Err.Description` は、外交官(ADO)が「向こうの国で問題が起きました」と報告してきた、要約された一言に過ぎません。

一方で、ADOの `Connection.Errors` コレクションには、サーバー側が吐き出した「どのテーブルの、どの制約に、どの値が原因で失敗したのか」という、詳細な報告書(エラーの詳細情報)が格納されているのです。

この「詳細な報告書」を読み解く技術こそが、今回お話しするテーマです。

2. 実装の核心:Errorsコレクションを解剖する

それでは、具体的なコードを見ていきましょう。
単にエラーを出すだけでなく、それを「テキストファイルに詳細なログとして書き出す」仕組みを作ります。

まずは、準備として、VBAエディタのメニュー [ツール] -> [参照設定] から 「Microsoft ActiveX Data Objects x.x Library」 にチェックを入れておいてくださいね。

実践:詳細エラーログ生成エンジン

Option Explicit

' ==============================================================================
' 関数名:LogADOErrors
' 目的  :ADO Connectionオブジェクトに蓄積された詳細なエラー情報をログ出力する
' 引数  :conn - エラーが発生したADO.Connectionオブジェクト
'         : filePath - ログを出力するテキストファイルのパス
' ==============================================================================
Sub LogADOErrors(ByRef conn As ADODB.Connection, ByVal filePath As String)
    Dim errObj As ADODB.Error
    Dim fileNum As Integer
    Dim logMsg As String
    
    ' エラーが一つもなければ終了
    If conn.Errors.Count = 0 Then Exit Sub
    
    fileNum = FreeFile
    
    ' ログファイルを開く(追記モード)
    Open filePath For Append As #fileNum
    
    Print #fileNum, "--------------------------------------------------"
    Print #fileNum, "【ERROR LOG】 発生日時: " & Now()
    Print #fileNum, "--------------------------------------------------"
    
    ' ADOのErrorsコレクションをループして、詳細情報をすべて書き出す
    ' ここがプロの技:Errオブジェクトではなく、conn.Errorsの中身を見る!
    For Each errObj In conn.Errors
        logMsg = "[Error Number]: " & errObj.Number & vbCrLf & _
                 "[SQL State]   : " & errObj.SQLState & vbCrLf & _
                 "[Description]: " & errObj.Description & vbCrLf & _
                 "[Source]      : " & errObj.Source
        
        Print #fileNum, logMsg
        Print #fileNum, "--------------------------------------------------"
    Next errObj
    
    Close #fileNum
    
    Debug.Print "詳細エラーログを生成しました: " & filePath
End Sub

' ==============================================================================
' メイン処理:トランザクションとエラーハンドリングの例
' ==============================================================================
Sub ExecuteSecureTransaction()
    Dim conn As ADODB.Connection
    Dim strConn As String
    Dim strSQL As String
    Dim logPath As String
    
    ' 設定
    strConn = "Provider=SQLOLEDB;Data Source=YOUR_SERVER;Initial Catalog=YOUR_DB;Integrated Security=SSPI;"
    logPath = CurrentProject.Path & "\db_error_log.txt"
    
    Set conn = New ADODB.Connection
    
    On Error GoTo ErrorHandler
    
    ' 1. 接続開始
    conn.Open strConn
    
    ' 2. トランザクションの開始(ここが重要!)
    ' 複数の処理を行う場合、一つでも失敗したら全てをなかったことにするために必要です
    conn.BeginTrans
    
    ' 3. SQL実行(例:わざと重複エラーを起こすようなSQL)
    strSQL = "INSERT INTO TargetTable (ID, Name) VALUES (1, 'Test User');"
    conn.Execute strSQL
    
    ' 4. コミット(成功したら確定)
    conn.CommitTrans
    MsgBox "処理が正常に完了しました。", vbInformation
    
CleanUp:
    ' 後片付け
    If Not conn Is Nothing Then
        If conn.State = adStateOpen Then conn.Close
    End If
    Set conn = Nothing
    Exit Sub

ErrorHandler:
    ' エラーが発生した場合
    ' まずはトランザクションをロールバック(取り消し)して、データの整合性を守る
    If Not conn Is Nothing Then
        If conn.State = adStateOpen Then
            conn.RollbackTrans
            Debug.Print "Transaction Rolled Back."
        End If
    End If
    
    ' 【ここが本番】ADOのErrorsコレクションを使って詳細ログを書き出す
    LogADOErrors conn, logPath
    
    ' ユーザーには分かりやすいメッセージを表示
    MsgBox "エラーが発生しました。詳細はログを確認してください。" & vbCrLf & _
           "Error No: " & Err.Number, vbCritical, "システムエラー"
           
    Resume CleanUp
End Sub

3. ここがプロのポイント:コードの解説

コードをただコピーするだけでなく、なぜこのように書くのか、その「意図」を理解しましょう。

① `conn.BeginTrans` と `conn.RollbackTrans`

これらは「トランザクション処理」と呼ばれるものです。
例えば、「在庫を減らす」処理と「売上を作る」処理の2つがあったとき、在庫だけ減って売上が作れなかったら大変ですよね?
トランザクションを使うことで、「どちらか一方が失敗したら、両方とも実行前の状態に戻す」という、データの整合性を守る強力な魔法が使えます。

② `For Each errObj In conn.Errors`

ここが今回の主役です。
通常の `Err.Description` は、VBAが受け取った「表面的なエラー」しか見ませんが、このループを使うことで、SQL Serverが送ってきた「内部的なエラーコード(SQLState)」や「詳細なエラー内容」を一つ残らず拾い上げることができます。

③ ログファイルへの追記 (`Append`)

`Open filePath For Append` とすることで、古いログを消さずに、新しいエラーをどんどん後ろに書き足していくことができます。これは、不具合の発生パターンを時系列で追うために非常に重要です。

4. 陥りやすいエラーと回避策

この仕組みを導入する際に、初心者がハマりやすいポイントが2つあります。

1. 参照設定の忘れ
`ADODB.Connection` という型を使っているため、参照設定(Microsoft ActiveX Data Objects)がされていないと、「ユーザー定義型が定義されていません」というエラーで動きません。
2. 接続が閉じているのにエラーを見ようとする
`LogADOErrors` に渡す `conn` オブジェクトは、接続が開いている状態(`adStateOpen`)でなければなりません。接続を閉じて(`conn.Close`)からエラーを見ようとしても、エラー情報も一緒に消えてしまいます。必ず `RollbackTrans` の直後、接続が生きている間にログを出力してください。

最後に:一歩先へ進むあなたへ

いかがでしたでしょうか。
単に「エラーが出たから直す」のではなく、「エラーの正体を暴き、記録に残す」。
この姿勢こそが、堅牢で信頼されるシステムを作るエンジニアの第一歩です。

今回紹介した `LogADOErrors` は、一度作ってしまえば、どんなプロジェクトでも使い回せる「武器」になります。ぜひ、あなたのプロジェクトに組み込んで、エラーに怯えない開発を楽しんでください。

ここをクリアすれば、Access VBAの基礎はバッチリですよ。
また次のレッスンでお会いしましょう!

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