Access VBAの暗黒面を制御せよ:QueryDefにおける「型不一致」を撲滅するアーキテクチャ設計
Access VBAで業務システムを構築する際、最も多くのエンジニアが泥沼にはまるのが「SQLの動的生成」だ。`”WHERE ID = ” & varID` のように文字列を連結してSQLを組んでいるなら、今すぐその手を止めてほしい。
「日付型がJet/ACEエンジンで解釈できない」「数値のカンマでクエリが破壊される」。こうした実行時エラーは、運が悪いのではない。設計が甘いだけだ。
今日は、QueryDefを極限まで安全に操り、型不一致を物理的に発生させないための「型マッピング・オートメーション」の極意を伝授する。
—
なぜ文字列連結によるSQL構築は「罪」なのか
初心者がやりがちな `SQL = “SELECT FROM T_Data WHERE Date = #” & myDate & “#”` という書き方。これには致命的な欠陥が2つある。
1. カルチャ依存の罠: PCの地域設定が変わっただけで日付フォーマット(MM/DD vs DD/MM)が逆転し、クエリが爆発する。
2. SQLインジェクションの脆弱性: ユーザー入力を直接連結すれば、悪意ある文字列でデータベースが蹂躙される。
これを回避する唯一にして最強の手段は、`QueryDef`オブジェクトと`Parameters`コレクションを正しく使いこなすことだ。
—
堅牢なSQL構築のための「型マッピング関数」実装
VBAの変数型を、Access(ACE)が解釈可能なSQL表現へ安全に変換するブリッジを用意する。重要なのは、「値そのもの」をSQLに埋め込むのではなく、「パラメーターとして渡す」ための準備を徹底することだ。
以下のコードは、あらゆるVBAの型を安全に`QueryDef`へ渡すためのプロトタイプである。
‘ @brief VBA型から適切なSQLパラメーターを定義し、QueryDefに注入する
‘ @param qdf 対象のQueryDefオブジェクト
‘ @param paramName パラメーター名
‘ @param val 値
Public Sub SetParameterSafe(ByRef qdf As QueryDef, ByVal paramName As String, ByVal val As Variant)
‘ Null許容の処理:DBの世界ではNullは値ではなく「状態」である
If IsNull(val) Then
qdf.Parameters(paramName).Value = Null
Exit Sub
End If
‘ 型ごとの厳密な代入処理
‘ QueryDefのParametersコレクションに直接代入することで、
‘ ACEエンジンが型変換を自動で行うため、文字列連結によるフォーマット崩れが皆無になる
Select Case VarType(val)
Case vbDate
qdf.Parameters(paramName).Value = CDate(val)
Case vbString
qdf.Parameters(paramName).Value = CStr(val)
Case vbInteger, vbLong, vbDouble, vbDecimal
qdf.Parameters(paramName).Value = CDec(val)
Case Else
qdf.Parameters(paramName).Value = val
End Select
End Sub
—
現場で即戦力となるQueryDef実行のベストプラクティス
上記関数を用いた、保守性が極めて高い実行メソッドがこれだ。
Public Sub ExecuteQuerySafe()
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim rs As DAO.Recordset
Set db = CurrentDb
‘ あらかじめ定義したパラメータークエリを指定
Set qdf = db.QueryDefs(“q_SalesFilter”)
‘ パラメーターを安全にセット
SetParameterSafe qdf, “[prmStartDate]”, DateAdd(“m”, -1, Date)
SetParameterSafe qdf, “[prmCategory]”, “Electronics”
‘ 実行
Set rs = qdf.OpenRecordset(dbOpenSnapshot)
‘ … ここでレコードセットを処理 …
rs.Close
Set rs = Nothing
Set qdf = Nothing
End Sub
—
アーキテクトからの助言:なぜこれが「勝てる」のか
この手法が優れている理由は、「SQL文字列をいじらない」という一点に尽きる。
- 保守性の向上: クエリのロジック(SQL)と、そこに渡すデータ(VBA)が完全に分離されている。SQL文を変更しても、VBA側のコードを書き直す必要はほとんどない。
- パフォーマンスの最適化: `QueryDef`はコンパイルされた状態で保持される。動的SQLを連結するたびにクエリプランが再生成される非効率から脱却できる。
- エラーの早期発見: `SetParameterSafe`内で型判定を行うことで、不正なデータがSQLに到達する前にVBAレベルで捕捉できる。
最後に
Access VBAは、プログラミング作法次第で、レガシーな「おもちゃ」にも、堅牢な「業務基盤」にもなる。
「動けばいい」というコードを卒業し、型を制御し、実行計画を意識する。この視点を持つだけで、あなたの書くコードの寿命は劇的に延びるはずだ。
次にコードを書くとき、`&`でSQLを繋ごうとしたら、この記事を思い出してほしい。その連結は、将来のバグへの招待状に他ならないのだから。
