Access VBAを掌握する:動的SQL生成の「実行時エラー」を撲滅するアーキテクチャ
現場で「なぜか動かないクエリ」に頭を抱えた経験はないだろうか。
Accessの `QueryDef` を動的に書き換える際、実行時に初めて発生する構文エラーは、システムの信頼性を根底から揺るがす。特に複雑な結合やサブクエリを含む動的SQLにおいて、実行まで成否が不明なアーキテクチャは、プロの現場では「負債」でしかない。
今日は、オブジェクトのライフサイクルを制御し、SQLの構文を「実行前」に検知する、伝説的なエンジニアのためのバリデーション・ロジックを伝授する。
—
1. なぜ「実行時エラー」を許してはならないのか
VBAにおける `QueryDef.SQL` の書き換えは、一見便利だが、Accessのデータベースエンジン(ACE/Jet)はクエリを保存・コンパイルするまでSQLの整合性を完全に保証しない。
多くのエンジニアは「とりあえず実行してエラーが出たら `On Error GoTo` で拾う」という場当たり的な実装に終始する。だが、これはオブジェクトの解放漏れや、トランザクションの不整合を引き起こす温床となる。我々が目指すべきは、SQLを生成した瞬間にその妥当性を検証し、失敗するクエリを門前払いする「検問所」の実装である。
—
2. SQL構文バリデーターの実装
ACEエンジンに直接クエリを解析させるために、ダミーの `QueryDef` を利用する手法が最も効率的だ。恒久的なクエリを汚染せず、メモリ上で完結させる。
‘ — SQL構文バリデータークラス: SqlValidator —
Option Explicit
‘ 実行前にSQLの正当性を検証する
Public Function IsValidSql(ByVal sqlStatement As String) As Boolean
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Set db = CurrentDb
‘ メモリ上に一時的なクエリ定義を作成(名前を空にすることで一時クエリとして扱う)
‘ ※dbQOpenTableなどのパラメータは不要。解析が目的のため。
On Error Resume Next
Set qdf = db.CreateQueryDef(“”, sqlStatement)
If Err.Number <> 0 Then
‘ ここでエラーログを吐き出すのがプロの仕事
Debug.Print “SQL Syntax Error: ” & Err.Description
IsValidSql = False
Else
IsValidSql = True
End If
On Error GoTo 0
‘ 厳密なオブジェクト解放。これを怠るとメモリリークの温床となる。
If Not qdf Is Nothing Then
qdf.Close
Set qdf = Nothing
End If
Set db = Nothing
End Function
—
3. シニアエンジニアが意識すべき「メモリの最適化」
上記のコードで重要なのは、`qdf.Close` を明示的に呼び出し、さらに `Set qdf = Nothing` で参照を破棄している点だ。Access VBAはガベージコレクションを信用してはならない。
特に大規模なシステムでは、`QueryDef` を生成するたびにメモリを消費し続ける。これを防ぐために、SQL生成ロジックと実行ロジックを完全に分離し、生成器(Generator)には一切のデータベース接続を持たせないのが鉄則だ。
—
4. 動的パラメータクエリの究極形
動的SQLで最も危険なのは「文字列連結」によるタイポやSQLインジェクションである。`QueryDef` の `Parameters` コレクションを正しく使うことで、これを回避できる。
‘ テンプレートSQLを動的にパラメータ化して実行する例
Public Sub ExecuteDynamicQuery(ByVal sqlTemplate As String, ParamArray params() As Variant)
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim i As Integer
If Not IsValidSql(sqlTemplate) Then Exit Sub
Set db = CurrentDb
Set qdf = db.CreateQueryDef(“”, sqlTemplate)
‘ パラメータのバインド
For i = LBound(params) To UBound(params)
qdf.Parameters(i).Value = params(i)
Next i
‘ 実行処理
qdf.Execute dbFailOnError
‘ 解放
qdf.Close
Set qdf = Nothing
Set db = Nothing
End Sub
—
結論:システムは「予測可能」でなければならない
優れたシステムとは、エラーが起きた時にどう対処するかではなく、「構造的にエラーが起き得ない状態」をいかに保つかで決まる。
- バリデーションを通さないクエリは実行させない。
- オブジェクトは生成したスコープ内で必ず解放する。
- 文字列連結ではなく、コレクションによるパラメータ受け渡しを徹底する。
これらはVBAというレガシーな環境であっても、現代の堅牢なソフトウェア開発の要諦と何ら変わらない。この知見を胸に、貴方のシステムを「止まらないエンジン」へと昇華させてほしい。
技術は裏切らない。コードの質が、そのシステムの寿命を決定づけるのだから。
