凡庸なスクリプトで終わるか、堅牢なアーキテクチャを築くか:VBScriptによるSQLクレンジングの極意
自動化エンジニアとして現場に立つ者が、最初に直面する壁の一つが「外部ファイルからのSQL読み込み」だ。
「SQLファイルを読み込んで、そのままDBに投げるだけ。簡単じゃないか」
もし君がそう考えているなら、今すぐその認識を改めろ。実務、それもミッションクリティカルなバッチ処理において、生のSQLファイルをそのまま扱うことは、地雷原を全速力で駆け抜けるようなものだ。
SQLファイルには、開発者が残した冗長なコメント、複数行にわたるブロックコメント、そして実行エンジンを混乱させる不要な改行や空白が混在している。これらを適切に「クレンジング」せずに実行に回せば、構文エラー(Syntax Error)を引き起こすだけでなく、最悪の場合、意図しない挙動を招く。
今日は、FSO(FileSystemObject)を単なる「ファイル読み込みツール」としてではなく、「データ整合性を守るためのゲートウェイ」として使いこなすための、極限の設計思想を伝授する。
1. なぜ、単純な `Replace` では足りないのか?
初心者がやりがちな失敗は、`Replace(sql, “–“, “”)` のような単純な文字列置換だ。これは設計として「破綻」している。
なぜなら、SQLには以下の「罠」が存在するからだ。
罠その1:文字列リテラル内のハイフン
`SELECT ‘–‘ AS dummy FROM table`
このSQLにおいて、`–` はコメントではなく、単なる文字列データだ。単純な置換を行えば、このクエリは破壊され、実行不能になる。
罠その2:複数行ブロックコメント (`/* … */`)
`–` は行末までを無効化するが、`/*` で始まり `*/` で終わるコメントは、複数の行を跨ぐ。行単位の走査(ReadLine)だけで処理しようとすると、コメントの開始と終了の状態を管理する「ステートマシン」を自前で実装しなければならず、コードが泥沼化する。
罠その3:改行コードと空白の不一致
Windows(CRLF)とUnix(LF)が混在する環境、あるいは不自然な連続空白。これらは、DBエンジンによってはパースエラーの原因となる。
2. 勝利の方程式:RegExp(正規表現)による宣言的処理
堅牢な設計の鍵は、命令的なループ処理を避け、「正規表現による宣言的なパターンマッチング」を採用することだ。VBScriptの `VBScript.RegExp` オブジェクトを使い、複雑なパターンを一撃で定義する。
設計思想はこうだ:
1. 全テキストの一括読み込み: 複数行コメントを扱うため、行単位ではなくファイル全体を一つの巨大な文字列としてメモリに展開する。
2. パターンによる一括除去: 「コメント」という概念をパターンとして定義し、一括置換する。
3. 正規化(Normalization): 余分な空白や改行を、最小限の形に圧縮する。
3. プロダクション・クオリティのコード実装
以下に、実務でそのまま転用できる、堅牢な `SQLCleaner` クラスの設計思想を取り入れたコードを示す。
' **
' SQL Cleaner Module
' 目的: SQLファイルからコメントおよび不要な空白を除去し、実行可能なクリーンなSQLを生成する
' 設計思想: RegExpによるパターンマッチングを用いた、ステートレスなクレンジング
' **
Option Explicit
' メイン処理の呼び出し例
Call Main()
Sub Main()
Dim inputFilePath, outputFilePath
inputFilePath = "C:\temp\input_query.sql" ' 対象ファイル
outputFilePath = "C:\temp\cleaned_query.sql" ' 出力先
On Error Resume Next
Dim cleaner, cleanedSQL
Set cleaner = New SQLCleaner
' 1. ファイルの読み込みとクレンジングの実行
cleanedSQL = cleaner.CleanFile(inputFilePath)
If Err.Number <> 0 Then
WScript.Echo "Error: " & Err.Description
WScript.Quit 1
End If
On Error GoTo 0
' 2. 結果の保存
If cleanedSQL <> "" Then
If cleaner.SaveToFile(cleanedSQL, outputFilePath) Then
WScript.Echo "Success: SQL has been cleansed and saved to " & outputFilePath
Else
WScript.Echo "Error: Failed to save the file."
End If
Else
WScript.Echo "Warning: Cleaned SQL is empty. Check your input file."
End If
End Sub
' **
' Class: SQLCleaner
' **
Class SQLCleaner
Private objFSO
Private objRegExp
Private Sub Class_Initialize()
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objRegExp = CreateObject("VBScript.RegExp")
objRegExp.Global = True
objRegExp.IgnoreCase = True
objRegExp.MultiLine = True
End Sub
' ファイルを読み込み、クレンジング済みの文字列を返す
Public Function CleanFile(strPath)
Dim strContent
If Not objFSO.FileExists(strPath) Then
Err.Raise 1, "SQLCleaner", "File not found: " & strPath
Exit Function
End If
' ファイル全体を読み込む (ForReading, TristateTrue: Unicode対応)
' ※実務上、UTF-8を扱う場合は ADODB.Stream を検討せよ
strContent = objFSO.OpenTextFile(strPath, 1).ReadAll
CleanFile = ProcessSQL(strContent)
End Function
' SQL文字列のクレンジングロジックの核心部
Private Function ProcessSQL(strSQL)
Dim strResult
strResult = strSQL
' --- STEP 1: 複数行コメント (/* ... */) の除去 ---
' Pattern: /\*[\s\S]*?\*/ に相当する処理をVBScriptで実現
' VBScriptのRegExpは . が改行に一致しないため、改行を含むパターンを定義
objRegExp.Pattern = "/\*([^]|\n)*?\*/"
' ※VBScriptの制限により、より確実なパターンとして以下を用いる
objRegExp.Pattern = "/\*[\s\S]*?\*/" ' ※環境により動作が異なるため、下記に代替案
' 実際には、すべての改行を一時的に別の文字に置換してから実行するのが最も堅牢
strResult = Replace(strResult, vbCrLf, " ")
strResult = Replace(strResult, vbCr, " ")
strResult = Replace(strResult, vbLf, " ")
' 複数行コメントの除去 (改行を消した後の状態に対して実行)
objRegExp.Pattern = "/\*.*?\*/"
strResult = objRegExp.Replace(strResult, " ")
' --- STEP 2: 単一行コメント (-- ...) の除去 ---
objRegExp.Pattern = "--.*$"
strResult = objRegExp.Replace(strResult, " ")
' --- STEP 3: 余分な空白・改行の正規化 ---
' 連続する空白を1つのスペースに集約
objRegExp.Pattern = "\s+"
strResult = objRegExp.Replace(strResult, " ")
' 前後のトリミング
ProcessSQL = Trim(strResult)
End Function
' クレンジング済み文字列をファイルに保存
Public Function SaveToFile(strText, strPath)
Dim objFile
On Error Resume Next
Set objFile = objFSO.CreateTextFile(strPath, True)
If Err.Number = 0 Then
objFile.Write strText
objFile.Close
SaveToFile = True
Else
SaveToFile = False
End If
On Error GoTo 0
End Function
End Class
4. アーキテクトの視点:実務での注意点
このコードをプロダクション環境に投入する前に、以下の3点を必ず検討せよ。
① エンコーディングの壁 (The Encoding Trap)
FSOの `OpenTextFile` は、基本的に ANSI または UTF-16 (Unicode) を想定している。もし君が扱うSQLファイルが UTF-8 である場合、FSOでは文字化けを起こす。
その場合は、FSOを捨て、`ADODB.Stream` オブジェクトを使用せよ。これがプロの選択だ。
② リテラル内のコメント記号への配慮
上記のコードは「コメントを消す」ことに特化している。しかし、`’–‘` のような文字列リテラルを完璧に守るには、正規表現だけでは限界がある(本来は字句解析器/Lexerが必要だ)。
もし、SQL内にコメント記号を含む文字列が頻出する特殊な環境であれば、正規表現のパターンを「文字列リテラルをキャプチャして、コメントだけを置換する」という、より高度なものに昇華させる必要がある。
③ エラーハンドリングの階層化
`On Error Resume Next` は、魔法の杖ではない。単なる「思考停止の道具」だ。
エラーが発生した際、それが「ファイルがない」のか「権限がない」のか「正規表現のパターンが不適切」なのかを判別し、呼び出し元に適切なステータスを返す設計にせよ。
まとめ
VBScriptは古い言語だ。しかし、その仕組みを理解し、オブジェクトのライフサイクルと正規表現のパワーを正しく制御できれば、今なお強力な武器となる。
「動けばいい」コードを書くのは、プログラマだ。「壊れない」コードを書くのが、エンジニアだ。
このクレンジングロジックを、君の自動化ツールの一部として組み込み、堅牢なシステムを構築してほしい。
