こんにちは!VBScriptの世界へようこそ。
「Excelマクロの自動記録は使えるようになったけれど、もっと本格的な自動化ツールを作りたい」「膨大なCSVファイルをデータベースに取り込む前処理を自動化したい」……そんなステップアップを目指すあなたのために、今回はVBScriptを使った本格的な「CSV型推論エンジン」の構築に挑戦します!
ヘッダー情報(列名)すら存在しない謎のCSVファイルが届いたとき、あなたならどうしますか? 1行ずつ人間が確認してデータベースのテーブル定義(`CREATE TABLE`文)を書くのは大変ですよね。
この記事では、先頭の数千行(サンプルデータ)を高速に走査し、各列が「数値(整数・小数)」「日付」「文字列」のどれに該当するかを自動判定して、最適なSQL文を吐き出す動的パーサーを一緒に作りましょう。
一見難しそうに見えますが、仕組みが分かればとてもシンプルです。「ここをクリアすれば、VBScriptの基本とデータ処理の本質はバッチリですよ!」と言えるレベルまで、優しく丁寧に解説していきますね。
—
1. 型推論エンジンの全体像(概念図)
まずは、今回作成するスクリプトがどのように動くのか、全体の処理フローを図解でイメージしてみましょう。
[ 巨大なCSVファイル ]
│
▼
┌─────────────────────────┐
│ 先頭1,000行だけをサンプリング │ ◄─ メモリ消費を最小限に抑える!
└──────────┬──────────────┘
│
▼
┌─────────────────────────┐
│ 1セルごとに型判定を実施 │
│ (IsNumeric / IsDate) │
└──────────┬──────────────┘
│
▼
┌─────────────────────────┐
│ 「型の昇格ピラミッド」判定 │ ◄─ 1つでも文字列が混ざればStringへ昇格
└──────────┬──────────────┘
│
▼
┌─────────────────────────┐
│ SQL DDL(CREATE TABLE) │
│ 文を動的に自動生成! │
└─────────────────────────┘
全件を読み込むのではなく、「先頭の一定行数(サンプル)」だけを高速走査するのがプロの現場の鉄則です。これにより、数ギガバイトあるような超巨大CSVであっても、一瞬で型推論を完了させることができます。
—
2. VBScriptにおける「型の昇格(Promotion)」ルール
型推論の心臓部となるのが「型の昇格ルール」です。
例えば、ある列の1行目が `100`(整数)だったとします。しかし、500行目に `123.45`(小数)が現れたら、その列は「整数型」から「小数型」に昇格しなければなりません。さらに900行目に `”エラー”` という文字が入っていたら、最終的にその列は「文字列型」として定義する必要があります。
この判定優先度(ピラミッド)を以下のように定義します。
1. UNKNOWN(未定):まだデータを見ていない状態
2. INTEGER(整数型):`IsNumeric` が True かつ 小数点がない
3. DOUBLE(浮動小数点型):`IsNumeric` が True かつ 小数点がある
4. DATE(日付型):`IsDate` が True
5. STRING(文字列型):上記以外、または矛盾が発生した場合の「最終退避先」
—
3. 完全実装:CSV型推論エンジン (VBScript)
それでは、実際に動作するコードを見ていきましょう!
テキストエディタ(メモ帳など)に貼り付け、拡張子を `.vbs` にして保存すれば、Windows環境でそのまま実行できます。
‘ ==============================================================================
‘ 【CSV dynamic Schema Parser & DDL Generator】
‘ 機能: CSVの先頭データを走査し、データ型を自動判定してCREATE TABLE文を生成する
‘ ==============================================================================
Option Explicit
‘ ——————————————————————————
‘ 定数定義(型の優先度)
‘ ——————————————————————————
Const TYPE_UNKNOWN = 0
Const TYPE_INTEGER = 1
Const TYPE_DOUBLE = 2
Const TYPE_DATE = 3
Const TYPE_STRING = 4
‘ 読み込み設定
Const SAMPLE_ROW_LIMIT = 1000 ‘ 走査するサンプル行数
Const CSV_FILE_PATH = “C:\data\sample.csv” ‘ 解析対象のCSVファイル名
‘ ——————————————————————————
‘ メイン処理を実行
‘ ——————————————————————————
Call Main()
Sub Main()
Dim fso, ts
Dim line, fields
Dim colProfiles()
Dim rowCount, colCount, i
Dim ddlSql
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ ファイルの存在確認
If Not fso.FileExists(CSV_FILE_PATH) Then
WScript.Echo “[ERROR] 指定されたCSVファイルが存在しません: ” & CSV_FILE_PATH
Exit Sub
End If
‘ ファイルを開く (1: ForReading)
Set ts = fso.OpenTextFile(CSV_FILE_PATH, 1, False)
rowCount = 0
colCount = 0
WScript.Echo “>>> スキーマ解析を開始します… (最大 ” & SAMPLE_ROW_LIMIT & ” 行)”
‘ 先頭から指定行数だけ走査(ストリーム読み込み)
Do While Not ts.AtEndOfStream And rowCount < SAMPLE_ROW_LIMIT
line = ts.ReadLine
' 空行のスキップ
If Trim(line) <> “” Then
‘ カンマ区切りで配列化(簡易パーサー)
fields = Split(line, “,”)
‘ 初回行の段階で列プロファイル配列を動的確保
If rowCount = 0 Then
colCount = UBound(fields)
ReDim colProfiles(colCount)
For i = 0 To colCount
Set colProfiles(i) = New ColumnInfo
colProfiles(i).ColumnName = “Col_” & (i + 1)
Next
End If
‘ 列ごとの型判定を実行
For i = 0 To colCount
If i <= UBound(fields) Then
colProfiles(i).EvaluateType Trim(fields(i))
End If
Next
rowCount = rowCount + 1
End If
Loop
' オブジェクトの後始末(明示的な解放)
ts.Close
Set ts = Nothing
Set fso = Nothing
' --------------------------------------------------------------------------
' 結果の出力(CREATE TABLE 文の生成)
' --------------------------------------------------------------------------
ddlSql = "CREATE TABLE [ImportedTable] (" & vbCrLf
For i = 0 To colCount
ddlSql = ddlSql & " [" & colProfiles(i).ColumnName & "] " & colProfiles(i).GetSqlType()
If i < colCount Then
ddlSql = ddlSql & ","
End If
ddlSql = ddlSql & " -- サンプル最大長: " & colProfiles(i).MaxLength & " 文字" & vbCrLf
Next
ddlSql = ddlSql & ");"
WScript.Echo vbCrLf & "=== 解析完了: CREATE TABLE SQL ===" & vbCrLf
WScript.Echo ddlSql
End Sub
' ==============================================================================
' クラス定義: 各列の型推論状態をカプセル化するクラス
' ==============================================================================
Class ColumnInfo
Public ColumnName
Public CurrentType
Public MaxLength
' 初期化処理
Private Sub Class_Initialize()
CurrentType = TYPE_UNKNOWN
MaxLength = 0
End Sub
' データを判定して型を「昇格」させるメソッド
Public Sub EvaluateType(ByVal rawVal)
Dim valLen, detectedType
' ダブルクォーテーションの除去
rawVal = Replace(rawVal, """", "")
valLen = Len(rawVal)
' 最大文字数の更新
If valLen > MaxLength Then
MaxLength = valLen
End If
‘ 空文字の場合は現在の型を維持
If rawVal = “” Then Exit Sub
‘ 1. 単一セルの型を判定
detectedType = DetectSingleType(rawVal)
‘ 2. 状態遷移(型の昇格ルール)の適用
Select Case CurrentType
Case TYPE_UNKNOWN
CurrentType = detectedType
Case TYPE_INTEGER
If detectedType = TYPE_DOUBLE Then
CurrentType = TYPE_DOUBLE
ElseIf detectedType <> TYPE_INTEGER Then
CurrentType = TYPE_STRING
End If
Case TYPE_DOUBLE
If detectedType <> TYPE_INTEGER And detectedType <> TYPE_DOUBLE Then
CurrentType = TYPE_STRING
End If
Case TYPE_DATE
If detectedType <> TYPE_DATE Then
CurrentType = TYPE_STRING
End If
Case TYPE_STRING
‘ 一度 STRING になったら以降は昇格(不変)
CurrentType = TYPE_STRING
End Select
End Sub
‘ 単一文字列の判定ロジック
Private Function DetectSingleType(ByVal val)
‘ 先頭の「0埋め数値(例: 00123)」は郵便番号やコード値のため文字列とみなす
If Len(val) > 1 And Left(val, 1) = “0” And IsNumeric(val) And InStr(val, “.”) = 0 Then
DetectSingleType = TYPE_STRING
Exit Function
End If
‘ 数値判定
If IsNumeric(val) Then
If InStr(val, “.”) > 0 Then
DetectSingleType = TYPE_DOUBLE
Else
DetectSingleType = TYPE_INTEGER
End If
Exit Function
End If
‘ 日付判定
If IsDate(val) Then
DetectSingleType = TYPE_DATE
Exit Function
End If
‘ どれにも当てはまらない場合は文字列
DetectSingleType = TYPE_STRING
End Function
‘ SQL用のデータ型文字列に変換
Public Function GetSqlType()
Select Case CurrentType
Case TYPE_INTEGER
GetSqlType = “INTEGER”
Case TYPE_DOUBLE
GetSqlType = “FLOAT”
Case TYPE_DATE
GetSqlType = “DATETIME”
Case Else
‘ 安全のため最大文字数の2倍(最低16文字)を割り当て
Dim varcharLen
varcharLen = MaxLength 2
If varcharLen < 16 Then varcharLen = 16
If varcharLen > 255 Then varcharLen = 255
GetSqlType = “VARCHAR(” & varcharLen & “)”
End Select
End Function
End Class
—
4. コードの解説と陥りやすい「罠」
このスクリプトには、VBScriptで安全かつ超高速にデータ処理を行うための技術が詰まっています。初心者の方が特にハマりやすいポイントを紐解いてみましょう!
1. 「0埋めデータ」の罠 (`00123` 問題)
VBScriptの `IsNumeric(“00123”)` は `True` を返します。
しかし、これを数値型(Integer)として扱うと、データベースに入れた時に `123` に変換され、先頭の「00」が消滅してしまいます!(商品コードや郵便番号などで大問題になります)。
コード内の以下のロジックでこれを防いでいます:
‘ 先頭が “0” で始まり、2文字以上ある数値は「文字列」として扱う
If Len(val) > 1 And Left(val, 1) = “0” And IsNumeric(val) And InStr(val, “.”) = 0 Then
DetectSingleType = TYPE_STRING
2. メモリリークを防ぐオブジェクトのライフサイクル
VBScript(WSH環境)では、`CreateObject` で生成したCOMオブジェクト(`FileSystemObject` やクラスインスタンス)のメモリ解放を怠ると、大量データ処理時にスクリプトが重くなります。
使い終わったオブジェクトは、必ず明示的に解放しましょう。
Set ts = Nothing
Set fso = Nothing
この「後始末の美学」を身につけると、一気にプロのコードらしくなりますよ!
3. クラス(`Class`)の活用
VBScriptで `Class`構文 を使ったことがありますか?
`ColumnInfo` というクラスを作って「列の状態(現在の型、最大文字数)」を閉じ込める(カプセル化する)ことで、メインの処理側が驚くほどスッキリ読みやすくなっています。
—
5. 実行方法と出力結果の例
例えば、以下のような `sample.csv` を用意してスクリプトを実行してみます。
【入力データ: sample.csv】
00981,100,2023/10/01,10.5,商品A
00982,200,2023-10-02,20.0,商品B
00983,3000000000,2023/10/03,30.25,非常に長い商品名のサンプルデータ
【実行コマンド】
コマンドプロンプトを開き、以下のコマンドを実行します。
cscript //NoLogo parse_schema.vbs
【出力結果】
>>> スキーマ解析を開始します… (最大 1000 行)
=== 解析完了: CREATE TABLE SQL ===
CREATE TABLE [ImportedTable] (
[Col_1] VARCHAR(16), — サンプル最大長: 5 文字
[Col_2] INTEGER, — サンプル最大長: 10 文字
[Col_3] DATETIME, — サンプル最大長: 10 文字
[Col_4] FLOAT, — サンプル最大長: 5 文字
[Col_5] VARCHAR(32) — サンプル最大長: 16 文字
);
見事に `Col_1`(先頭0埋めコード)は `VARCHAR`、`Col_2` は `INTEGER`、`Col_3` は `DATETIME` として自動認識されました!
—
6. まとめ:ここをクリアすればVBScriptの基礎はバッチリ!
今回は、VBScriptを使った動的スキーマ解析(CSV型推論エンジン)の構築にチャレンジしました。
この記事で学んだ重要ポイントを振り返ってみましょう:
- ストリーム読み込み: 全件ではなく先頭サンプルだけを読み込んで高速処理
- 型の昇格ロジック: 判定の優先度(Integer → Double → String)を設計する
- エッジケースの考慮: 先頭の `0` 埋めデータなどを文字列として逃がす工夫
- オブジェクトのライフサイクル: `Set obj = Nothing` による適切なメモリ解放
これらはVBScriptに限らず、PythonやC#、VBAなどあらゆるプログラミング言語に応用できる「データ処理の本質」です。
マクロの記録から一歩踏み出し、こうした構造化されたロジックが書けるようになれば、あなたのVBScript技術はもう初学者レベルを完全に脱しています。自信を持って、日々の業務自動化に役立ててくださいね!
何か分からない点があれば、いつでも質問してください。応援しています!
