業務自動化の最前線に立つ諸君、そして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パターンを導入するだけで、あなたのツールは「素人の作った便利ツール」から「プロが設計した業務基幹システム」へと昇華する。
文字列結合の「& _」地獄で消耗するのは今日で終わりにしよう。
堅牢なクラスを一つ設計する。そのわずかな手間が、将来のあなたを、そしてあなたのコードを保守する後任者を救うことになるのだ。
「動くのは当たり前だ。美しく、かつ揺るぎないものを作れ。」
これが、現場を支配するアーキテクトの思考である。
