Access VBAの「文字列連結地獄」から脱却せよ:DAO.QueryDefを掌握するSQLビルダー設計術
Access開発の現場で、一度は誰もが絶望する瞬間がある。
`strSQL = “SELECT FROM T_Order WHERE OrderDate BETWEEN #” & Format(dtStart, “yyyy/mm/dd”) & “# AND …”`
――この、引用符(”)とアンパサンド(&)が乱舞する「文字列連結の迷宮」だ。
可読性は皆無。数週間後に保守しようとすれば、どこでダブルクォートが閉じられているのか判別できず、スペースの欠落によるSQLエラーに時間を溶かす。
「文字列連結によるSQL生成」は、今日で卒業しよう。
今回は、DAO.QueryDefを操るための「SQLビルダー・クラス」の設計思想と、実務で即戦力となる実装コードを伝授する。
—
1. なぜ「文字列連結」は悪なのか
単に「見にくい」からではない。真の理由は「状態の分離ができていないから」だ。
SQL文を文字列として直接連結すると、以下のリスクが常に付きまとう。
- 型の不一致: 日付や数値を文字列に変換するロジックが散乱し、ローカライズやバリデーションの漏れが生じる。
- SQLインジェクションのリスク: 動的クエリの生成において、エスケープ処理を忘れれば脆弱性の温床となる。
- 修正の連鎖: カラム名一つを変えるだけで、コード全体に漂う文字列を一つずつ追う必要がある。
プロのエンジニアは「SQLの組み立て」と「データの注入」を厳格に分離する。これこそが、堅牢なAccessシステム構築の第一歩だ。
—
2. SQLビルダー・クラスの設計指針
今回作成する `SqlBuilder` クラスの核となる思想は「Fluent Interface(流れるようなインターフェース)」と「自動エスケープ」である。
設計のポイント
1. 内部バッファの隠蔽: SQLを内部の `Collection` や `String` 変数で保持し、`ToString` メソッドで確定させる。
2. パラメータの型安全: 日付なら `#` で囲む、文字列なら `’` で囲む処理をメソッド内に封じ込める。
3. DAO.QueryDefとの融合: 生成されたSQLを即座に `QueryDef.SQL` プロパティへ流し込める状態にする。
—
3. 【プロダクション実装】SqlBuilder クラス
クラスモジュール(名前:`SqlBuilder`)として保存してほしい。
‘ SqlBuilderクラス
Option Compare Database
Option Explicit
Private m_Sql As String
‘ 構築開始
Public Function SelectFrom(table As String) As SqlBuilder
m_Sql = “SELECT FROM ” & table
Set SelectFrom = Me
End Function
‘ 条件の追加(AND接続を前提としたシンプルな実装例)
Public Function Where(field As String, value As Variant) As SqlBuilder
m_Sql = m_Sql & ” WHERE ” & field & ” = ” & FormatValue(value)
Set Where = Me
End Function
‘ 値の型に応じた適切なエスケープ処理
Private Function FormatValue(value As Variant) As String
Select Case VarType(value)
Case vbString: FormatValue = “‘” & Replace(value, “‘”, “””) & “‘”
Case vbDate: FormatValue = “#” & Format(value, “yyyy/mm/dd hh:nn:ss”) & “#”
Case Else: FormatValue = value
End Select
End Function
‘ SQL文字列の取得
Public Function ToString() As String
ToString = m_Sql
End Function
—
4. 実戦投入:DAO.QueryDefでの活用例
このクラスを使うと、メインの処理は驚くほどスッキリする。
Sub UpdateReportQuery(targetDate As Date)
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim builder As New SqlBuilder
Set db = CurrentDb
‘ SQLの組み立て(可読性が段違い)
Dim sql As String
sql = builder.SelectFrom(“T_Sales”) _
.Where(“SalesDate”, targetDate) _
.ToString()
‘ QueryDefの更新
Set qdf = db.QueryDefs(“qry_DynamicReport”)
qdf.SQL = sql
‘ 必要に応じてDoCmd.OpenQueryなどで実行
Debug.Print “SQL Updated: ” & qdf.SQL
Set qdf = Nothing
Set db = Nothing
End Sub
—
5. 伝説的エンジニアからの忠告
この設計を導入するにあたり、以下の「重み」を忘れないでほしい。
- QueryDefのキャッシュ: `QueryDef` のSQLを頻繁に書き換えると、Accessの内部キャッシュが肥大化し、データベースが破損するリスクがある。頻繁な書き換えが必要なら、テンポラリテーブルへデータをインポートし、そこをクエリで参照する設計へ切り替える勇気を持つこと。
- パフォーマンスの罠: `Select ` は禁じ手だ。必要最小限のカラムのみを `Select` するメソッドを `SqlBuilder` に追加し、パフォーマンスを担保せよ。
- トランザクション: もしこの動的クエリが更新系(`UPDATE` / `DELETE`)を伴うなら、必ず `dbEngine(0).BeginTrans` で囲むこと。自動生成されたSQLこそ、予期せぬ範囲に破壊的影響を及ぼす可能性がある。
コードとは、単に動くものを書く作業ではない。「未来の自分が読んだときに、一瞬で意図を理解できること」こそが、最高峰のエンジニアが守るべき唯一の規律である。
さあ、今すぐその「文字列連結」を削除し、設計されたコードで現場を圧倒しよう。
