【テクニカル・上級編】ワイルドカード検索を安全に実装する:LIKE演算子とパラメータクエリの融合 – Access VBA解析バイブル

スポンサーリンク

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という枯れた環境において、いかにモダンな設計思想を適用できるか。それは言語の制限ではなく、エンジニア自身の「美学」の問題である。

今すぐ君のコードを見直し、文字列結合を排除せよ。そこから、真のプロフェッショナルな開発が始まる。

タイトルとURLをコピーしました