ようこそ、Access VBAの深淵へ。私はあなたの冒険を支えるチーフアーキテクトです。
Accessでの開発を進めていくと、必ずぶつかる大きな壁があります。それは「ユーザーの入力条件に合わせて、実行するSQLを動的に変えたい」という場面です。
多くの人が、コードの中で `strSql = strSql & ” WHERE …”` と、延々と文字列を繋ぐ「文字列結合の迷宮」に迷い込み、クォーテーションの数やスペースの入れ忘れで数時間を溶かしてしまいます。
今日は、その迷宮を脱出し、誰が見ても美しく、メンテナンスしやすいSQL生成を実現する「StringBuilderパターンのヘルパークラス」を伝授しましょう。ここをマスターすれば、あなたのAccess開発は「マクロの延長」から「プロフェッショナルなソフトウェア開発」へと進化します。
—
1. なぜ「QueryDef」と「動的SQL」の相性は最悪なのか?
Accessには、作成済みのクエリを管理する `QueryDef` というオブジェクトがあります。これは実行速度が速く、非常に優秀な仕組みです。
しかし、VBAから `QueryDef.SQL` を書き換えようとすると、途端にコードが汚くなります。
‘ よくある「悪い」例:読みづらく、バグの温床
strSql = “SELECT FROM T_在庫 ”
strSql = strSql & “WHERE 商品名 LIKE ‘” & Me.txtSearch & “‘ ”
If Me.chkOnlyStock.Value = True Then
strSql = strSql & “AND 在庫数 > 0 ”
End If
‘ ↑ スペースが足りなかったり、ANDの条件管理が複雑になると破綻する
このような「その場しのぎの連結」を卒業するために、SQLを組み立てる専用の道具(クラス)を作りましょう。
—
2. 魔法の杖:SqlBuilderクラスを設計する
まずは、面倒な文字列連結をすべて引き受けてくれる「執事」のようなクラスを作成します。
クラスモジュールの作成
1. VBE(Alt + F11)を開きます。
2. 挿入メニューから「クラスモジュール」を選択。
3. プロパティウィンドウでオブジェクト名を `clsSqlBuilder` に変更します。
以下のコードをコピー&ペーストしてください。
Option Compare Database
Option Explicit
‘ — clsSqlBuilder: SQL構築を劇的に楽にするクラス —
Private mSql As String
Private mWhereCount As Integer
‘ 最初に呼び出す初期化メソッド
Public Sub Clear()
mSql = “”
mWhereCount = 0
End Sub
‘ 単純に文字列を追加する(前後にスペースを自動で挟む)
Public Sub Append(ByVal text As String)
mSql = mSql & ” ” & Trim(text) & ” ”
End Sub
‘ WHERE句をスマートに追加する
‘ 最初の1回目は WHERE を、2回目以降は AND を自動付与する
Public Sub AddCondition(ByVal condition As String)
If mWhereCount = 0 Then
mSql = mSql & ” WHERE ” & condition
Else
mSql = mSql & ” AND ” & condition
End If
mWhereCount = mWhereCount + 1
End Sub
‘ 最終的なSQLを書き出す
Public Function Build() As String
‘ 余計な空白を整理して返す
Build = Replace(Trim(mSql), ” “, ” “)
End Function
このコードの「極限の知見」ポイント
- スペースの自動制御: `Append` メソッドが前後にスペースを入れるため、`FROMTable` のように単語がくっついてエラーになるのを防ぎます。
- 状態の保持: `mWhereCount` を持つことで、開発者が「今は最初の条件かな? 2番目かな?」と `AND` を付けるかどうか悩む必要をなくしています。
—
3. 実戦投入:QueryDefを動的に書き換える
それでは、作成したクラスを使って、検索フォームのボタンをクリックした時にクエリを書き換えるコードを書いてみましょう。
Private Sub btnSearch_Click()
Dim sb As New clsSqlBuilder
Dim qdf As DAO.QueryDef
‘ 1. SQLの基本部分を構築
sb.Clear
sb.Append “SELECT FROM T_商品マスタ”
‘ 2. 画面の入力状況に応じて条件を「直感的」に追加
‘ 商品名の入力があれば抽出条件に加える
If Nz(Me.txtProductName, “”) <> “” Then
sb.AddCondition “商品名 LIKE ‘” & Me.txtProductName & “‘”
End If
‘ カテゴリの選択があれば抽出条件に加える
If Nz(Me.cmbCategory, “”) <> “” Then
sb.AddCondition “カテゴリID = ” & Me.cmbCategory
End If
‘ 在庫ありチェックボックスがONの場合
If Me.chkInStock = True Then
sb.AddCondition “在庫数 > 0”
End If
‘ 3. QueryDefに流し込む
On Error GoTo Err_Handler
‘ すでに作成済みのクエリ「qrySearchResult」を操作
Set qdf = CurrentDb.QueryDefs(“qrySearchResult”)
qdf.SQL = sb.Build()
‘ クエリを表示(またはサブフォームを再クエリ)
DoCmd.OpenQuery “qrySearchResult”
Set qdf = Nothing
Exit Sub
Err_Handler:
MsgBox “SQL生成エラー: ” & Err.Description, vbCritical
End Sub
—
4. なぜこの方法が「最強」なのか
この設計を導入することで、あなたは以下の3つの苦しみから解放されます。
1. 「スペース不足」エラーの消滅
`”SELECT FROM” & “Table”` が `”SELECT FROMTable”` になる悲劇はもう起こりません。
2. 「WHERE 1=1」という古いハックの卒業
よく見かける `WHERE 1=1`(常に真の条件を置いて、後は全部 `AND` で繋ぐ手法)を使わなくても、スマートに `WHERE` と `AND` を使い分けられます。
3. 可読性の劇的な向上
`If` 文の中が「条件そのもの」だけに集中しているため、後から仕様変更(「やっぱり価格の範囲指定も追加して」など)があっても、数行足すだけで完了します。
—
5. 初学者が陥りやすい落とし穴とアドバイス
最後に、この手法を使う上での「先輩からのアドバイス」です。
- データ型に注意!:
SQLを組み立てる際、数値型なら `ID = 1` で良いですが、文字列型なら `Name = ‘田中’` のようにシングルクォーテーションが必要です。日付型なら `#2023/10/01#` です。これだけは、`AddCondition` に渡す文字列の中で意識してください。
- QueryDefの永続性:
`qdf.SQL = …` と書くと、Accessの中に保存されているクエリ定義そのものが書き換わります。これは非常に強力ですが、複数のユーザーが同時に同じクエリを書き換えると競合します。分割データベース(フロントエンドを各PCに配布)にする運用を心がけましょう。
まとめ
Access VBAの世界は、ただ「動けばいい」コードを書く段階から、「意図が伝わる美しい構造」を作る段階へ進むと、一気に楽しくなります。
今回紹介した `SqlBuilder` クラスは、その第一歩です。複雑なパズルを解くような文字列結合はやめて、論理を組み立てるプログラミングを楽しみましょう。これができれば、あなたのAccess VBAの基本はもう完璧と言っても過言ではありません。
次は、このクラスに「パラメーターのバインド機能」を追加する応用編でお会いしましょう。ハッピーコーディング!
