【実務・中級編】QueryDefのSQLプロパティを「動的生成」する際の文字列連結を劇的に簡素化するヘルパークラスの設計 – Access VBA解析バイブル

スポンサーリンク

業務自動化の最前線に立つ諸君、そしてAccessという「枯れているが最強のラピッドデベロップメントツール」を武器に戦うエンジニア諸君。

今日は、Access VBA開発における「最大の汚点」を浄化する話をしよう。

プログラムを組んでいて、以下のようなコードを一度でも書いた(あるいは目にした)ことはないか?

‘ 典型的な「スパゲッティSQL」
strSql = “SELECT FROM T_Order ”
strSql = strSql & “WHERE OrderDate >= #” & txtFrom & “# ”
If chkFlag = True Then
strSql = strSql & “AND Status = ‘Completed’ ”
End If
‘ 以下、延々と続く & _ の地獄…

断言しよう。この「文字列結合によるSQL構築」こそが、バグの温床であり、保守性を破壊する元凶だ。クォーテーションの閉じ忘れ、不適切なスペースの挿入、可読性の欠如。これらはすべて、設計の欠如から来る低俗な問題だ。

今回は、この混沌を終わらせる「SqlBuilderクラス」を設計し、DAOの`QueryDef`を真に使いこなすための極限の知見を伝授する。

—

1. なぜ「文字列結合」を捨て、クラスを設計すべきなのか

Access VBAにおいてSQLを動的に生成する際、我々が直面するのは「文字列としての正しさ」と「SQLとしての正しさ」の二正面作戦だ。

1. スペースの制御: `AND`の前にスペースを入れ忘れて構文エラーになる。
2. 動的なWHERE句: 最初の条件だけ `WHERE` で、次からは `AND` にするという条件分岐がコードを汚す。
3. エスケープ処理: シングルクォートを含む文字列が飛んできた瞬間に実行時エラーが起きる。

これらを解決するのがStringBuilderパターンの導入だ。状態をクラス内に隠蔽し、メソッドを通じてSQLを組み上げることで、呼び出し側のコードを宣言的(Declarative)かつクリーンに保つことができる。

—

2. 極限のSqlBuilderクラス:`clsSqlBuilder`

まずはこのコードをクラスモジュールとして作成してほしい。クラス名は `clsSqlBuilder` とする。

Option Compare Database
Option Explicit

‘ ===========================================================================
‘ Class: clsSqlBuilder
‘ Description: SQL構築における文字列結合の複雑性を隠蔽し、可読性と堅牢性を担保する
‘ ===========================================================================

Private m_lines As Collection
Private m_whereClauses As Collection

Private Sub Class_Initialize()
Set m_lines = New Collection
Set m_whereClauses = New Collection
End Sub

‘ — SQLの基本句を追加 —
Public Sub Add(ByVal sqlPart As String)
m_lines.Add Trim(sqlPart)
End Sub

‘ — WHERE句を動的に追加(自動的にANDで結合される) —
Public Sub AddWhere(ByVal condition As String)
If Len(Trim(condition)) > 0 Then
m_whereClauses.Add “(” & Trim(condition) & “)”
End If
End Sub

‘ — SQLの最終組み立て —
Public Function Build() As String
Dim sql As String
Dim i As Long

‘ 基本句の結合
For i = 1 To m_lines.Count
sql = sql & m_lines(i) & ” ”
Next i

‘ WHERE句の結合
If m_whereClauses.Count > 0 Then
sql = sql & “WHERE ”
For i = 1 To m_whereClauses.Count
sql = sql & m_whereClauses(i)
If i < m_whereClauses.Count Then sql = sql & " AND " End If Next i End If Build = Trim(sql) End Function ' --- 文字列リテラルの安全なエスケープ(ユーティリティ) --- Public Function Quote(ByVal val As Variant) As String ' Access SQL形式に準拠 If IsNull(val) Then Quote = "NULL" ElseIf IsDate(val) Then Quote = "#" & Format(val, "yyyy/mm/dd HH:nn:ss") & "#" ElseIf IsNumeric(val) Then Quote = CStr(val) Else Quote = "'" & Replace(val, "'", "''") & "'" End If End Function ---

3. QueryDefとSqlBuilderを組み合わせた実践的実装

次に、このクラスを用いて「一時的なQueryDef」を生成し、レコードセットを取得する標準モジュールの例を見てみよう。

ここで重要なのは、QueryDefに名前を付けない(空文字にする)ことだ。これにより、データベース内にゴミを残さず、メモリ上だけでクエリ定義を完結させることができる。

Public Sub ExecuteDynamicSearch()
Dim sb As New clsSqlBuilder
Dim qdf As DAO.QueryDef
Dim rs As DAO.Recordset

‘ 1. SQLの構造を定義(可読性が飛躍的に向上する)
sb.Add “SELECT T_Order.OrderID, T_Order.CustomerName, T_Order.Amount”
sb.Add “FROM T_Order”

‘ 2. 検索条件の動的構築(条件がある場合だけ追加)
‘ ※UIのコントロール値に応じて柔軟に組み立てる
If Not IsNull(Me.txtCustomerName) Then
sb.AddWhere “CustomerName LIKE ” & sb.Quote(“” & Me.txtCustomerName & “”)
End If

If Not IsNull(Me.txtMinAmount) Then
sb.AddWhere “Amount >= ” & Me.txtMinAmount
End If

‘ 3. QueryDefの生成(名前を空にすることで一時オブジェクト化)
Set qdf = CurrentDb.CreateQueryDef(“”, sb.Build())

‘ ログ出力(デバッグ効率が圧倒的に高い)
Debug.Print “Generated SQL: ” & qdf.SQL

‘ 4. 実行
Set rs = qdf.OpenRecordset(dbOpenSnapshot)

‘ 結果の処理(例:サブフォームに表示、あるいは集計)
‘ Set Me.subForm.Form.Recordset = rs

‘ 後処理
rs.Close
qdf.Close
Set rs = Nothing
Set qdf = Nothing
End Sub

—

4. プロの視点:なぜこの設計が「最強」なのか

① 「WHERE 1=1」という無様なハックからの脱却

初心者はよく `WHERE 1=1` と書き、その後に `AND …` を繋げる手法を取る。しかし、この `SqlBuilder` は内部で `WHERE` と `AND` の付与タイミングを制御している。これにより、生成されるSQLは常に美しく、最適化される。

② QueryDefオブジェクトのライフサイクル管理

`DoCmd.RunSQL` や `db.Execute(sql)` を直接叩くのは二流だ。
`QueryDef` を介することで、実行前にSQLを確認でき、さらに実行計画の再利用(コンパイル済みクエリの恩恵)を最大限に享受できる。特に大量のデータを扱う際、この僅かな差がユーザー体験の差となって現れる。

③ エスケープ漏れによるSQLインジェクションの防止

`sb.Quote()` メソッドを用意したことに注目してほしい。AccessはデスクトップDBとはいえ、シングルクォートが入力されただけでクラッシュするツールを納品するのはプロ失格だ。このクラスを使えば、型に応じた適切なクォート処理が自動的に行われる。

—

結論:コードの美しさはシステムの堅牢さに直結する

Access VBAは、その手軽さゆえに「汚いコード」が放置されがちな領域だ。しかし、今回紹介したようなBuilderパターンを導入するだけで、あなたのツールは「素人の作った便利ツール」から「プロが設計した業務基幹システム」へと昇華する。

文字列結合の「& _」地獄で消耗するのは今日で終わりにしよう。
堅牢なクラスを一つ設計する。そのわずかな手間が、将来のあなたを、そしてあなたのコードを保守する後任者を救うことになるのだ。

「動くのは当たり前だ。美しく、かつ揺るぎないものを作れ。」

これが、現場を支配するアーキテクトの思考である。

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