Access VBAを掌握する:動的SQLとパラメータクエリの「安全な融合」という真実
Access開発において、動的SQLを文字列結合で組み立てるという「アンチパターン」は、今すぐ墓場に埋めるべきだ。それは単にSQLインジェクションの脆弱性を生むだけではない。クエリプランの再利用性を損ない、実行計画のキャッシュを破棄し、結果としてシステムのパフォーマンスを肥大化させる。
今回は、`QueryDef` オブジェクトとパラメータクエリを駆使し、ワイルドカード検索を「安全かつ高速」に実装する極限のテクニックを伝授する。
—
1. なぜ「文字列結合」が罪深いのか
多くの初心者は `WHERE Field LIKE ‘” & Me.txtKey & “‘”` のようにSQLを構築する。これには二つの致命的な欠陥がある。
1. セキュリティ: 悪意のある入力を排除できない。
2. 実行計画の劣化: SQL文字列が変わるたびに、Access(Jet/ACEエンジン)は新しいクエリとしてコンパイルをやり直す。数千回のクエリ発行が必要なバッチ処理でこれを行うのは、エンジンに対して鈍器を振り回すようなものだ。
我々が目指すべきは、SQL構造を固定し、パラメータの型と値だけを注入することである。
—
2. パラメータクエリとLIKE演算子の「正しい」マリッジ
パラメータクエリにおいて、`LIKE` 演算子とワイルドカード(“)を組み合わせる際、最も陥りやすい罠は「パラメータの型定義」だ。
以下のコードは、効率と安全性を両立させるためのテンプレートである。
Public Sub ExecuteSearch(ByVal keyword As String)
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim rs As DAO.Recordset
‘ オブジェクトのライフサイクルを厳密に管理する
Set db = CurrentDb
‘ 事前に定義したパラメータクエリを呼び出す
Set qdf = db.QueryDefs(“qry_SearchTemplate”)
‘ パラメータにワイルドカードを組み込んでセットする
‘ ここで重要なのは、値そのものにワイルドカードを含めること
qdf.Parameters(“[p_Keyword]”).Value = “” & keyword & “”
‘ パフォーマンスの極致:ForwardOnly/ReadOnlyでオーバーヘッドを殺す
Set rs = qdf.OpenRecordset(dbOpenForwardOnly, dbReadOnly)
‘ ここでデータを処理(バインドや出力など)
‘ …
‘ 【重要】明示的な解放。VBAのガベージコレクションを待つ余裕はない
rs.Close
Set rs = Nothing
qdf.Close
Set qdf = Nothing
Set db = Nothing
End Sub
なぜこの実装が最強なのか?
- クエリプランの固定: `qry_SearchTemplate` は一度コンパイルされたプランを使い続ける。
- 型安全: パラメータとして値を渡すため、SQLエンジンは入力を単なる「リテラル値」として扱い、コマンドの注入を防ぐ。
- リソース管理: 明示的に `Set = Nothing` を行うことで、Accessのメモリ空間に居座るオブジェクトの寿命を制御している。
—
3. レガシー環境とWindows APIの静かなる連携
もし、この検索結果を大容量の外部連携(CSV出力や他システムへのパイプライン送出)に使う場合、VBA標準の関数だけではメモリリークに足をすくわれる。
特に、検索対象が数万件を超える場合、`DoEvents` を適切に挟みつつ、OSのメモリ解放を促す API を併用するのが真のプロの所作だ。
‘ 宣言部
Private Declare PtrSafe Sub SetProcessWorkingSetSize Lib “kernel32” _
(ByVal hProcess As LongPtr, ByVal dwMinimumWorkingSetSize As Long, _
ByVal dwMaximumWorkingSetSize As Long)
‘ メモリ最適化が必要な場面で呼び出す
Public Sub CompactMemory()
SetProcessWorkingSetSize -1, -1, -1
End Sub
この API は、プロセスが不要に確保した物理メモリをOSに返還させる。「なぜAccessが数時間でメモリを数GB食い潰すのか」という問いに対する、我々からの回答がこれだ。
—
4. アーキテクトからの提言:クエリ定義の「定数化」
最後に、一つだけ。`QueryDef` をコードの中に埋め込む(`db.CreateQueryDef`)のは、デバッグの悪夢を招く。
私は、「クエリは常にデータベース内の『クエリ定義』として分離せよ」と助言する。
VBA側は、パラメータの値を送り込み、結果を受け取る「コントローラー」に徹するべきだ。ロジックとデータアクセス層の分離こそが、10年後もメンテナンス可能なレガシーシステムを作る唯一の道である。
Accessという枯れた環境において、いかにモダンな設計思想を適用できるか。それは言語の制限ではなく、エンジニア自身の「美学」の問題である。
今すぐ君のコードを見直し、文字列結合を排除せよ。そこから、真のプロフェッショナルな開発が始まる。
