泥沼のSQL連結から脱却せよ:DAO.QueryDefを操る「Fluent SQL Builder」の極意
Access開発の現場で、未だに `strSQL = “SELECT FROM T_Table WHERE ID = ” & Me.txtID & ” AND Status = ‘” & strStatus & “‘”` といった「文字列連結のスパゲッティ」を量産している諸君へ。
そのコードは、保守のたびに開発者の精神を削り、メンテナンス不能なレガシーの墓標を積み上げているに過ぎない。シニアエンジニアであれば、SQL構築の責務を「文字列の継ぎ接ぎ」から「オブジェクトの構築」へと昇華させるべきだ。
本稿では、`DAO.QueryDef`のSQLプロパティを動的に制御する際、メモリ効率と可読性を極限まで高めるための「Fluent SQL Builder」設計パターンを伝授する。
—
なぜ「文字列連結」が罪深いのか
文字列連結(`&`演算子)は、VBAのメモリ管理において最悪の選択肢の一つだ。連結のたびに新しい文字列メモリがヒープ領域に確保され、古い文字列がガーベジコレクション(またはVBAの簡易的なメモリ管理)の負荷となる。
特に複雑な動的クエリを生成する場合、構文エラーの温床となる「スペース漏れ」や「クォーテーションの迷宮」に陥る。我々が目指すべきは、SQLを構造体として扱い、必要なタイミングで文字列へシリアライズする設計だ。
—
実装:SQLBuilderクラスの設計方針
このクラスは、SQLの断片を内部的にコレクションとして保持し、`Build`メソッドが呼ばれた瞬間にのみ連結を行う。これにより、メモリの断片化を最小限に抑える。
SQLBuilder.cls
‘ @Description: SQL構築をカプセル化し、メモリ効率と保守性を最適化するクラス
Option Compare Database
Option Explicit
Private m_Select As String
Private m_From As String
Private m_Where As Collection
Private Sub Class_Initialize()
Set m_Where = New Collection
End Sub
Public Sub SelectClause(ByVal fields As String)
m_Select = fields
End Sub
Public Sub FromClause(ByVal table As String)
m_From = table
End Sub
‘ 動的条件を安全にスタックする
Public Sub AddCondition(ByVal condition As String)
m_Where.Add condition
End Sub
‘ SQLの最終生成(DAO.QueryDefに渡す直前に実行)
Public Function Build() As String
Dim sql As String
sql = “SELECT ” & m_Select & ” FROM ” & m_From
If m_Where.Count > 0 Then
Dim i As Long
sql = sql & ” WHERE ”
For i = 1 To m_Where.Count
sql = sql & m_Where(i) & IIf(i < m_Where.Count, " AND ", "")
Next
End If
Build = sql
End Function
Private Sub Class_Terminate()
Set m_Where = Nothing
End Sub
---
現場で「極限のパフォーマンス」を叩き出すための運用指針
このクラスを使うだけでは不十分だ。真のアーキテクトは、`DAO.QueryDef`のライフサイクルを完全に掌握する。
1. QueryDefの再利用と明示的解放
`CurrentDb.CreateQueryDef`をループ内で乱用してはならない。それはTempDBの肥大化と断片化を招く。`QueryDef`オブジェクトは一度生成し、`SQL`プロパティを書き換えて再利用(`db.QueryDefs(“qdfName”).SQL = builder.Build`)するのが定石だ。
2. パラメータの注入は「型」を意識せよ
`AddCondition`に文字列を放り込む際、必ず型変換関数(`Format`や`CStr`)を噛ませろ。特に日付型はローカル環境の差異が深刻なバグを呼ぶ。`Format(dateValue, “\#yyyy-mm-dd\#”)` といった定型をクラス内にメソッドとして用意しておくべきだ。
3. 大規模なSQLに対するWinAPI活用
もし、生成するSQLが数千文字を超えるような巨大なクエリを扱う場合、VBAの内部文字列処理能力がボトルネックになる。その場合は `StrPtr` を用いたメモリ操作や、`StringBuilder`的なアプローチ(VB.NETの流儀をVBAで模倣する)を検討せよ。
—
結論:保守とは「変更を恐れない構造」を作ること
あなたが書いたコードは、あなたがいなくなった後の後任者がメンテナンスする。
「文字列連結」という原始的な手法に頼ることは、後任者に「クォーテーションの海」で溺れろと言っているに等しい。
今回提示した`SQLBuilder`クラスは、始まりに過ぎない。
1. 責務の分離(SQL構築ロジックをビジネス層から切り離す)
2. 型の安全性(パラメータクエリへの昇華)
3. リソース管理(QueryDefのライフサイクル管理)
この3点を守り抜くことこそが、Access開発における「プロフェッショナリズム」の証明だ。さあ、今すぐプロジェクト内のクエリ生成コードをリファクタリングせよ。技術的負債は、放置した瞬間に複利で膨らむのだから。
