【入門編】ADOの接続エラーを詳細にキャッチ:Errorsコレクションによる原因特定とログ出力 – Access VBA解析バイブル

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

「エラー番号だけ」で諦めていないか?ADOのErrorsコレクションで、サーバーの悲鳴を読み解く技術

こんにちは。今日もデータの海を渡るエンジニアの皆さん、お疲れ様です。

Access VBAを使って、SQL Serverなどの外部データベースと連携するシステムを構築していると、必ずと言っていいほど「壁」にぶつかる瞬間があります。

それは、「実行時エラー 3704: オブジェクトが開いていません」や「実行時エラー -2147467259: 接続できませんでした」といった、無機質で、どこか突き放したようなエラーメッセージです。

「なぜ繋がらないのか?」「サーバー側で何が起きているのか?」
VBAの標準的な `Err.Number` や `Err.Description` だけを見ている限り、あなたは霧の中で出口を探しているようなものです。

今日は、そんな「情報の欠落」を克服し、プロフェッショナルなエンジニアへとステップアップするための極意、ADOの `Errors` コレクションを活用した詳細なエラーキャッチ術を伝授しましょう。

ここをマスターすれば、あなたのデバッグ作業は「勘」から「確信」へと変わりますよ。

1. なぜ、VBAの `Err` オブジェクトだけでは足りないのか?

まず、アーキテクチャの観点から整理しておきましょう。ここを理解するのが、脱・初心者への第一歩です。

DAOとADOの決定的な違い

Accessでよく使われる DAO (Data Access Objects) は、Accessのテーブル(ローカル)を操作するための「家の中の道具」です。一方で、ADO (ActiveX Data Objects) は、SQL ServerやOracleといった「外の世界」へ繋がるための「架け橋」です。

「情報の二重構造」を知る

外部データベースと通信するとき、エラーは二段階で発生します。

1. 通信レイヤー(ADO/OLEDB)のエラー: ネットワークが切れた、認証に失敗した、サーバーがダウンした。
2. データベースエンジン(SQL Server等)のエラー: 制約違反(主キー重複)、データ型不一致、権限不足。

VBAの `Err` オブジェクトが拾えるのは、主に「1」の、いわば「通信が失敗したという事実」だけです。しかし、真の原因である「2」の「サーバーが具体的に何を嫌がっているのか」という詳細なメッセージは、ADOのオブジェクトの中に、`Errors` コレクションという形で大切に保管されているのです。

これを無視して `MsgBox Err.Description` だけを表示するのは、医者に「腹が痛い」と言った時に、医者が「痛みがありますね」とだけ返して診断を終えるようなものです。私たちは「なぜ痛いのか」を知る必要があるのです。

2. 実践:Errorsコレクションを解剖する

では、具体的にどのようにして、隠されたエラーメッセージを引き出すのか。
現場でそのまま使える、堅牢なエラーハンドリングのパターンを見ていきましょう。

以下のコードは、SQL Serverへの接続を試み、失敗した際にその詳細な理由(エラー番号、メッセージ、SQLの状態など)をすべてログに出力するサンプルです。

Option Explicit

''' <summary>
''' 外部データベースへの接続を試行し、詳細なエラーをログ出力するプロシージャ
''' </summary>
Public Sub ConnectToExternalDB()
    Dim conn As ADODB.Connection
    Dim strConn As String
    Dim errLoop As ADODB.Error
    Dim strLogPath As String
    
    ' 接続文字列(環境に合わせて書き換えてください)
    strConn = "Provider=SQLOLEDB;Data Source=YourServerName;Initial Catalog=YourDBName;User ID=YourUser;Password=YourPassword;"
    
    ' ログ出力先のパス
    strLogPath = CurrentProject.Path & "\db_error_log.txt"
    
    Set conn = New ADODB.Connection
    
    On Error GoTo ErrorHandler

    ' --- 接続試行 ---
    Debug.Print "接続を開始します..."
    conn.Open strConn
    
    ' 接続成功時の処理
    MsgBox "接続に成功しました!", vbInformation
    
    ' 後処理
    conn.Close
    Set conn = Nothing
    Exit Sub

ErrorHandler:
    ' --- ここからがプロの仕事 ---
    
    ' 1. まずは標準的なVBAエラーを記録
    Dim msg As String
    msg = "【VBA Error】" & vbCrLf & _
          "Number: " & Err.Number & vbCrLf & _
          "Description: " & Err.Description & vbCrLf
    
    ' 2. ADOのErrorsコレクションを精査する
    ' 接続オブジェクト(conn)が生成されていれば、その中に詳細が詰まっている可能性がある
    If Not conn Is Nothing Then
        If conn.Errors.Count > 0 Then
            msg = msg & vbCrLf & "--- ADO Detailed Errors ---" & vbCrLf
            
            ' Errorsコレクションをループして、全ての詳細を抜き出す
            For Each errLoop In conn.Errors
                msg = msg & "Error #" & errLoop.Number & vbCrLf & _
                      "Source: " & errLoop.Source & vbCrLf & _
                      "Description: " & errLoop.Description & vbCrLf & _
                      "SQL State: " & errLoop.SQLState & vbCrLf & _
                      "Native Error: " & errLoop.NativeError & vbCrLf & _
                      "----------------------------" & vbCrLf
            Next errLoop
        End If
    End If
    
    ' 3. ログファイルへの書き出し(実務ではテーブルへの書き込みも検討してください)
    Call WriteLog(strLogPath, msg)
    
    ' 4. ユーザーへの通知(詳細すぎる情報は隠し、要点だけを伝えるのが親切)
    MsgBox "データベース接続中に問題が発生しました。詳細はログを確認してください。", vbCritical, "接続エラー"
    
    ' クリーンアップ
    If Not conn Is Nothing Then
        If conn.State = adStateOpen Then conn.Close
        Set conn = Nothing
    End If
End Sub

''' <summary>
''' ログをテキストファイルに追記するヘルパー関数
''' </summary>
Private Sub WriteLog(ByVal filePath As String, ByVal logText As String)
    Dim fileNum As Integer
    fileNum = FreeFile
    
    Open filePath For Append As #fileNum
    Print #fileNum, "========================================"
    Print #fileNum, "Timestamp: " & Now()
    Print #fileNum, logText
    Print #fileNum, "========================================"
    Close #fileNum
End Sub

3. コードの解説:何が「プロ」の差を生むのか

このコードには、初学者が陥りがちなミスを防ぐための「設計思想」が組み込まれています。

① `Errors` コレクションのループ処理

`For Each errLoop In conn.Errors` の部分に注目してください。
一つの操作(例えば、複雑なストアドプロシージャの実行など)に対して、複数のエラーが連鎖して発生することがあります。`Errors.Count` を確認し、ループで回すことで、「最初のエラー」だけでなく「その原因となったエラー」まで芋づる式に特定できるのです。

② `SQLState` と `NativeError` の取得

ここが最も重要なポイントです。

  • `SQLState`: ODBC標準で定義されたエラーコード。これを知っていれば、Google検索で世界中のエンジニアと同じ情報にアクセスできます。
  • `NativeError`: SQL Serverなどが独自に発行するエラー番号。これこそが、サーバー内部の真実を語るコードです。

③ ユーザー体験(UX)への配慮

エラーが起きたとき、`MsgBox` で「Native Error: 2627, SQL State: 23000…」なんて表示してはいけません。一般ユーザーはパニックになります。
「エラーの詳細はログに書く。画面には『問題が起きたこと』と『どう対処すべきか(ログを見てください)』だけを表示する」。これが、信頼されるシステムの作法です。

4. 陥りやすい罠:トランザクションとエラー

最後に、一つだけ高度なアドバイスを。
ADOを使って更新処理(INSERT/UPDATE)を行う際、`conn.BeginTrans` でトランザクションを開始している場合は、エラーハンドラの中で必ず `conn.RollbackTrans` を呼び出すようにしてください。

エラーが起きたのにトランザクションが「開いたまま」になると、データベースのテーブルがロックされ続け、他のユーザーが一切操作できなくなるという、最悪の事態を招くことがあります。

ErrorHandler:
    ' トランザクション中であればロールバック
    If conn.State = adStateOpen Then
        ' 接続状態を確認しつつ、トランザクションが開始されていれば戻す
        ' (実際にはフラグ変数などで管理するのがより安全です)
        On Error Resume Next ' ロールバック自体の失敗を防ぐ
        conn.RollbackTrans
        On Error GoTo 0
    End If
    ' ... 以下、ログ出力処理

まとめ:エラーは「敵」ではなく「ガイド」である

エラーが発生したとき、「ああ、また動かない」と落ち込む必要はありません。
エラーメッセージは、システムがあなたに送っている「ここを直せば動くよ」という親切なガイドなのです。

`Errors` コレクションを使いこなし、サーバーの言葉を正しく翻訳できるようになれば、あなたの開発スピードとシステムの信頼性は劇的に向上します。

「ただ動くコード」から「壊れても原因がすぐわかるコード」へ。
一歩ずつ、着実に進んでいきましょう。応援していますよ!

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