【実務・中級編】DoCmd.TransferSpreadsheetの「インポートエラーテーブル」を監視する自動例外処理 – Access VBA解析バイブル

スポンサーリンク

【Access VBA極限解説】DoCmd.TransferSpreadsheetの「インポートエラー」を完全に制御する例外処理アーキテクチャ

こんにちは。チーフアーキテクトの私だ。

日々の業務自動化において、ExcelファイルからAccessへのデータインポートは避けて通れない王道処理だ。
そこで多用するのが `DoCmd.TransferSpreadsheet` だろう。1行のコードで数千行のデータを吸い上げられるため、一見すると非常に便利に見える。

しかし、実務の現場でこのメソッドを「無防備」に叩いているとしたら、それは時限爆弾を抱えているのと同義だ。

Excel側のデータ型不一致、桁あふれ、必須項目の欠落などが発生した際、Accessは親切心から「インポート エラー」という名前のテーブルを勝手に生成し、処理を続行する。
結果として、ユーザーはデータが正常に取り込まれたと錯覚し、後続の集計処理で大爆発を起こす。この「サイレント・フェイル(沈黙の失敗)」こそが、現場を疲弊させる元凶なのだ。

今回は、この厄介なエラーテーブルのライフサイクルを完全に掌握し、プロダクション環境で耐えうる堅牢な例外監視メカニズムを構築する方法を伝授する。

なぜ「エラーテーブルの放置」は悪なのか?

`TransferSpreadsheet` でエラーが発生した際、Accessは元のテーブル名(例: `T_売上データ`)に倣い、`T_売上データ インポート エラー` という名前のテーブルを自動生成する。

素人がやりがちな非効率なアプローチはこれだ:

  • 「インポート後にエラーテーブルの有無をチェックしていない」
  • 「エラーテーブルができていることすら知らず、データが欠損したまま処理が進む」
  • 「エラー内容を確認するためだけに、わざわざAccessのナビゲーションウィンドウを開いて目視確認させる」

これでは「自動化」の名が泣く。
プロのエンジニアであれば、「エラーが発生した事実をコードで即座に検知し、自動でエラーログを抽出し、ユーザーに対処法をポップアップで突きつける」ところまでをワンストップで実装しなければならない。

堅牢なインポート監視アーキテクチャの設計思想

今回構築するモジュールの設計方針は以下の3点だ。

1. 事前クリーンアップの徹底
インポート処理の直前に、過去に残骸として残っている同名のエラーテーブルをあらかじめ物理削除しておく。これにより、「今回発生したエラーなのか、前回以前の残りカスなのか」の判定ミスを防ぐ。
2. CurrentDbとDAOによるトランザクション/オブジェクト操作
UIの遅延や予期せぬロックを防ぐため、データベースへのアクセスは `CurrentDb` を用いた明示的なオブジェクト操作を行う。
3. ユーザーフレンドリーな例外フィードバック
エラーテーブルが存在した場合、単に処理を中断するのではなく、エラーの総数と具体的な内容をメッセージボックス(またはログ)として整形し、業務担当者に即座にフィードバックする。

【実装コード】コピペで使えるプロダクションコード

以下のコードを標準モジュールに配置してほしい。
実務でそのまま組み込めるよう、エラーハンドリングとオブジェクトの解放(解放の作法)まで完璧に組み込んである。

Option Compare Database
Option Explicit

‘ ==============================================================================
‘ 処理名 : SafeImportSpreadsheet
‘ 概要 : Excelファイルを安全にインポートし、エラーテーブルを自動監視・通知する
‘ 引数 : strFilePath – インポートするExcelファイルのフルパス
‘ strTableName – 格納先のテーブル名
‘ ==============================================================================
Public Sub SafeImportSpreadsheet(ByVal strFilePath As String, ByVal strTableName As String)

Dim db As DAO.Database
Dim strErrorTableName As String
Dim tdf As DAO.TableDef
Dim rstError As DAO.Recordset
Dim lngErrorCount As Long
Dim strMsg As String

‘ エラーテーブル名の規則(Accessの仕様に依存)
strErrorTableName = strTableName & ” インポート エラー”

Set db = CurrentDb

On Error GoTo ErrorHandler

‘ ————————————————————————–
‘ 1. 事前クリーンアップ:過去のエラーテーブルが存在すれば削除する
‘ ————————————————————————–
If TableExists(strErrorTableName) Then
db.TableDefs.Delete strErrorTableName
End If

‘ ————————————————————————–
‘ 2. スプレッドシートのインポート実行
‘ ※ acSpreadsheetTypeExcel12Xml は Excel 2007以降 (.xlsx) の場合
‘ ————————————————————————–
DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel12Xml, _
strTableName, strFilePath, True

‘ ————————————————————————–
‘ 3. エラーテーブルの生成有無を監視
‘ ————————————————————————–
If TableExists(strErrorTableName) Then
‘ エラーテーブルが存在する場合 = インポート時にデータ破棄・型不一致が発生
Set rstError = db.OpenRecordset(“SELECT FROM [” & strErrorTableName & “]”, dbOpenSnapshot)

‘ レコード数を正確に取得
If rstError.RecordCount > 0 Then
rstError.MoveLast
lngErrorCount = rstError.RecordCount

‘ ユーザーへのフィードバックメッセージ構築
strMsg = “【警告】Excelデータのインポート中に ” & lngErrorCount & ” 件のエラーが発生しました。” & vbCrLf & _
“データ型の一致や桁あふれの可能性があります。” & vbCrLf & vbCrLf & _
“確認用テーブル: ” & strErrorTableName & vbCrLf & _
“該当データを修正して再実行してください。”

MsgBox strMsg, vbCritical + vbOKOnly, “インポート例外検知”

‘ ここで処理を中断(必要に応じてログ出力やロールバック処理を記述)
GoTo Finally
End If
Else
‘ 完全成功
MsgBox “データのインポートが正常に完了しました。”, vbInformation + vbOKOnly, “完了”
End If

GoTo Finally

ErrorHandler:
‘ 予期せぬVBAランタイムエラー(ファイルが存在しない、排他制御エラーなど)
MsgBox “致命的なエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “システムエラー”

Finally:
‘ ————————————————————————–
‘ リソースの解放(メモリリークの完全防止)
‘ ————————————————————————–
If Not rstError Is Nothing Then
rstError.Close
Set rstError = Nothing
End If
Set db = Nothing
End Sub

‘ ==============================================================================
‘ 補助関数 : 指定したテーブルが現在のデータベースに存在するか判定する
‘ ==============================================================================
Private Function TableExists(ByVal strTblName As String) As Boolean
Dim tdf As DAO.TableDef
TableExists = False

For Each tdf In CurrentDb.TableDefs
If tdf.Name = strTblName Then
TableExists = True
Exit For
End If
Next tdf
End Function

コードのアーキテクチャ的解説(プロがこだわるポイント)

1. `TableExists` による事前の残骸除去

Accessは、インポートエラーが起きるたびにエラーテーブルを自動生成する。もし前回の実行時に生成されたエラーテーブルがそのまま残っていると、今回のインポートでエラーが起きなかったとしても「エラーが存在する」と誤認してしまうリスクがある。
そのため、処理の冒頭で `TableExists` 関数を挟み、過去の残骸を完全に掃除してから `TransferSpreadsheet` を叩く設計にしている。

2. スナップショットによる高速なレコードカウント

エラーテーブルの件数を取得する際、無駄にテーブルを開いてフルスキャンするのはパフォーマンスの低下を招く。ここでは `dbOpenSnapshot` を指定し、メモリ上で読み取り専用の軽量なレコードセットを開くことで、パフォーマンスを極限まで高めている。

3. 確実なオブジェクトの解放(Finallyブロックの徹底)

VBAにおいて、`DAO.Recordset` や `DAO.Database` のオブジェクト変数を適切に閉じず、メモリ上に放置することは「メモリリーク」を引き起こし、Accessの動作不安定化(挙動の肥大化・破損)の直結原因となる。
意図したルートであっても、エラー発生時であっても、必ず `Finally:` ラベルへジャンプし、リソースを完全に解放するイディオムを徹底している。

まとめ:自動化の信頼性は「例外のハンドリング」で決まる

動くだけのコードを書くのはアマチュアだ。
「失敗したときにシステムがどう振る舞うべきか」を設計し尽くすことこそが、我々プロの業務自動化エンジニアの仕事である。

今回紹介したインポートエラーの自動監視アーキテクチャを導入すれば、「気づいたらデータが抜けていた」という現場の絶望を根絶することができる。ぜひ、あなたの開発するAccessアプリケーションに組み込んでほしい。圧倒的な堅牢性を実感できるはずだ。

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