【入門編】【実務】CSVインポート時にテーブル定義を自動拡張する柔軟なデータ取込機能 – Access VBA解析バイブル

スポンサーリンク

こんにちは!開発現場で日々、Accessと格闘している君なら、一度はこんな絶望的な状況に出くわしたことがあるはずだ。

「取引先から送られてきた毎月のCSV、今月から新しい項目(列)が勝手に追加されてるんだけど……!」

これを従来の固定化されたインポート定義やSQLで処理しようとすると、Accessは容赦なくエラーを吐き、業務はそこでストップ。慌ててテーブルデザインを開いてフィールドを手動で追加して……なんて、エンジニアとしてあまりにスマートじゃないよね。

今回は、CSVの進化(列の追加)にテーブル側が勝手に追従し、「データが存在しないなら、自動でフィールドを生やしてインポートを完遂する」という、実務で無類の強さを発揮する「自己進化型インポートエンジン」の極意を伝授しよう。

ここをクリアすれば、君のAccess VBAスキルは間違いなく一段上のステージへ到達する。さあ、一緒に扉を開けよう!

なぜ「静的なテーブル設計」はCSVインポートで破綻するのか?

Access VBA初学者がやりがちなのが、あらかじめ決まったフィールド数(例: Field1Field10)を持つテーブルに対して、DoCmd.TransferTextなどで決め打ちでインポートする方法だ。

しかし、世の中のCSVは生き物だ。
「来月から備考欄を追加してほしい」「担当者コードの列を挟んだ」――そんな仕様変更のたびにプログラムを修正していたら、保守だけで人生が終わってしまう。

私たちが目指すべきは、「CSVのヘッダー(1行目)を正義とし、テーブル側に足りないピースがあれば、VBAが裏側で自動的にパーツ(フィールド)を組み上げる」という動的なアプローチだ。

攻略の全体像:自動拡張インポートのアルゴリズム

今回構築する仕組みのフローはこうだ。

1. CSVの1行目(ヘッダー)を読み込む
2. Accessの対象テーブルのフィールド一覧と比較する
3. 「CSVにあって、テーブルにない」フィールドを見つけたら、即座に `TableDef` を操作して追加する
4. 準備が整ったテーブルへ、安全にデータを流し込む

これを実現するために、Access VBAの隠し武器である「DAO(Data Access Objects)」のテーブル定義変更機能(`TableDef` と `Field` オブジェクト)を召喚する。

実装コード:実務で即戦力になる「自動拡張インポート」プロシージャ

以下のコードを、君のAccessデータベースの標準モジュールに貼り付けてほしい。
実務でそのまま使えるよう、エラーハンドリングとコメントを徹底的に施してある。

Option Compare Database
Option Explicit

‘ ==============================================================================
‘ 【テーマ】CSVの変更を恐れない!テーブル自動拡張インポート機能
‘ ==============================================================================
Public Sub AutoExpandImportCSV()
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
Dim fso As Object
Dim ts As Object
Dim targetTable As String
Dim csvPath As String
Dim headerLine As String
Dim headers() As String
Dim i As Long
Dim fieldExists As Boolean

‘ — 設定エリア —
targetTable = “T_売上データ”
csvPath = CurrentProject.Path & “\sales_data.csv” ‘ 同一フォルダ内のCSVを想定

Set db = CurrentDb

‘ 1. FileSystemObjectでCSVの1行目(ヘッダー)を安全に取得する
Set fso = CreateObject(“Scripting.FileSystemObject”)
If Not fso.FileExists(csvPath) Then
MsgBox “指定されたCSVファイルが見つかりません: ” & csvPath, vbCritical, “エラー”
Exit Sub
End If

Set ts = fso.OpenTextFile(csvPath, 1) ‘ 1 = ForReading
If ts.AtEndOfStream Then
MsgBox “CSVファイルが空です。”, vbCritical, “エラー”
ts.Close
Exit Sub
End If

headerLine = ts.ReadLine
ts.Close

‘ カンマ区切りでヘッダーを配列に展開
headers = Split(headerLine, “,”)

‘ 2. テーブルの存在チェック&なければ新規作成
On Error Resume Next
Set tdf = db.TableDefs(targetTable)
On Error GoTo 0

If tdf Is Nothing Then
‘ テーブル自体がない場合は、最低限の器として新規作成する
Set tdf = db.CreateTableDef(targetTable)
‘ まずはIDフィールドだけでも作っておく
Set fld = tdf.CreateField(“ID”, dbLong)
fld.Attributes = dbAutoIncrField ‘ 1始まりのオートナンバー
tdf.Fields.Append fld
db.TableDefs.Append tdf
Set tdf = db.TableDefs(targetTable) ‘ 再取得
End If

‘ 3. 【核心】CSVのヘッダーとテーブルのフィールドを突合し、不足分を追加
db.Execute “BEGIN TRANSACTION”, dbFailOnError ‘ トランザクションで安全性を担保

On Error GoTo ErrorHandler

For i = LBound(headers) To UBound(headers)
‘ ダブルクォーテーションが含まれている場合は除去しておく
Dim colName As String
colName = Replace(headers(i), “”””, “”)

‘ テーブル内に同名のフィールドが存在するか走査
fieldExists = False
For Each fld In tdf.Fields
If StrComp(fld.Name, colName, vbTextCompare) = 0 Then
fieldExists = True
Exit For
End If
Next fld

‘ 存在しない場合のみ、新しくフィールドを追加する(既定はテキスト型・長さ255)
If Not fieldExists Then
Set fld = tdf.CreateField(colName, dbText, 255)
tdf.Fields.Append fld
Debug.Print “【自動拡張】新規フィールドを追加しました: ” & colName
End If
Next i

db.Execute “COMMIT”, dbFailOnError

‘ 4. インポート実行(既存のTransferTextなどへ繋ぎ込む)
‘ ※実際の運用ではここでDoCmd.TransferTextやSQLインサートを使用します
MsgBox “テーブルの構造確認および自動拡張が完了しました!”, vbInformation, “成功”

Exit Sub

ErrorHandler:
db.Execute “ROLLBACK”, dbFailOnError
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”
End Sub

レイヤーの高いエンジニアなら気づいたはずだが、このコードのキモは `TableDef` オブジェクトを操作した瞬間にデータベースのスキーマが書き換わる という点だ。

ここがプロのこだわり:トランザクションとパフォーマンス

テーブル定義の変更(DDL的な操作)は、Accessの内部エンジン(ACE)にとって比較的重い処理だ。さらに、予期せぬ文字化けやファイル破損で途中でエラーが起きた場合、中途半端にフィールドが追加された「汚れたテーブル」が残ってしまう。

だからこそ、上記のコードでは `BEGIN TRANSACTION` と `ROLLBACK` を挟み、「安全に拡張できなければ、すべての変更をロールバックする」という堅牢な設計にしている。実務で求められるのは、動的であることと同時に「絶対に壊れない安心感」なのだから。

初学者がハマりやすい「3つの罠」と回避策

この自動拡張ロジックを組む際、多くの人が以下の壁にぶつかる。先輩からのアドバイスとして心に留めておいてほしい。

1. フィールド名の文字数・禁則文字の罠

  • CSVのヘッダーにスペースや記号(「売上(円)」など)が含まれていると、Accessのフィールド名としてエラーになることがある。
  • 対策: ヘッダー文字列を配列に格納する際、記号をアンダースコア `_` に置換するサニタイズ処理を挟むと完璧だ。

2. データ型の自動判定の泥沼

  • すべてのフィールドを `dbText` (テキスト型) で作っているのには理由がある。数値や日付としてインポートしたい場合も、最初の受入段階ではすべてテキスト型で受け止め、後続のクエリで適切な型にキャスト(変換)するのが、インポートエラーを防ぐ最善のセオリーだ。

3. 排他制御の罠

  • 他のユーザーやフォームが対象テーブルを開いている(デザインビューを開いている等)状態でこのコードを実行すると、`TableDef` の書き換え時にエラー(実行時エラー 3211など)が発生する。
  • 対策: インポート処理を実行する前には、必ず該当テーブルを使用している画面を閉じる運用にするか、エラーハンドラーで優しくユーザーに促すこと。

まとめ:変化を恐れないシステムを作ろう

今回は、CSVの仕様変更にしなやかに追従する「テーブル自動拡張インポート」の極意を解説した。

  • 静的な設計に縛られるな。CSVの形に合わせてテーブル側を動的に育てろ。
  • DAOの `TableDef` と `Fields.Append` を使えば、VBAから自由にフィールドを生やせる。
  • 動的な処理ほど、トランザクションによる安全性の確保を忘れるな。

このテクニックをマスターすれば、「仕様変更です!」という突然の連絡にも、コーヒーでも飲みながら「あ、自動で拡張されるんでそのままで大丈夫ですよ」と涼しい顔で返せるようになる。

ここをクリアした君は、もう単なるマクロの記録者じゃない。立派なAccess業務自動化エンジニアだ。
さあ、自分の開発環境にこのコードを組み込んで、その爆発的な便利さを体感してほしい!

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