表面的なエラーに惑わされないために。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の基礎はバッチリですよ。
また次のレッスンでお会いしましょう!
