【テクニカル・上級編】QueryDefのSQLプロパティを動的に書き換える際の「構文エラー」をVBAで事前検知するバリデーター作成 – Access VBA解析バイブル

スポンサーリンク

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というレガシーな環境であっても、現代の堅牢なソフトウェア開発の要諦と何ら変わらない。この知見を胸に、貴方のシステムを「止まらないエンジン」へと昇華させてほしい。

技術は裏切らない。コードの質が、そのシステムの寿命を決定づけるのだから。

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