【Access VBA】SQLインジェクションを撲滅せよ:QueryDefパラメータによる「動的クエリ」の極意
Access開発の現場で、未だに「文字列連結」でSQLを組み立てている者はいないだろうか。
‘ 【絶対禁止】やってはいけない連結SQL
SQL = “SELECT FROM T_受注 WHERE 顧客ID = ” & Me.txtID
この書き方は、初心者には簡単に見えるかもしれない。だが、これは「セキュリティの穴」を自ら開けているに等しい。さらに言えば、クエリの実行プランがキャッシュされず、パフォーマンス上の損失も大きい。
真のエンジニアは、QueryDefオブジェクトのParametersコレクションを使いこなす。今回は、Access VBAにおける「安全かつ高速」な動的クエリ構築の極限を伝授する。
—
なぜ「文字列連結」は悪なのか
1. セキュリティリスク(SQLインジェクション): ユーザー入力に特殊文字(`’` や `;` など)が含まれていた場合、クエリ構造が破壊される。悪意あるコマンドを注入されれば、DB内の全データが流出・削除される危険がある。
2. 実行プランの非効率性: 文字列連結で生成されるSQLは、実行のたびに「異なるクエリ」として認識される。Accessエンジンは毎回コンパイルをやり直すため、高負荷な環境では目に見えて処理が遅延する。
3. 型変換の地獄: 日付や数値のフォーマットを自前で制御するのは、バグの温床だ。
—
解決策:QueryDefとParametersの正しい作法
SQLを文字列で連結するのではなく、SQLテンプレートを作成し、値をパラメータとして流し込む。これがAccessにおける正攻法だ。
実践的なプロダクションコード
以下の関数は、汎用的に使えるクエリ実行のテンプレートである。
‘ @Description: パラメータ付きクエリを実行し、レコードセットを返す堅牢な関数
‘ @Param qdfName: 既存のクエリ定義名
‘ @Param params: パラメータ値の配列
Public Function GetSecureRecordset(ByVal qdfName As String, ParamArray params() As Variant) As DAO.Recordset
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim i As Integer
Set db = CurrentDb
Set qdf = db.QueryDefs(qdfName)
‘ パラメータのクリア(キャッシュの影響を防ぐ)
‘ ※一度実行したqdfはパラメータが残るため再利用時は注意
‘ パラメータを順にバインドする
For i = LBound(params) To UBound(params)
‘ Parametersコレクションに値を代入
‘ 型変換はAccessエンジンが自動で安全に行う
qdf.Parameters(i).Value = params(i)
Next i
‘ パラメータ付きでクエリを実行
Set GetSecureRecordset = qdf.OpenRecordset(dbOpenSnapshot)
‘ オブジェクトの解放は呼び出し元で行うのが基本だが、
‘ ここではqdfを閉じてメモリを保護する
Set qdf = Nothing
End Function
使用例:フォームからの呼び出し
Private Sub btnSearch_Click()
Dim rs As DAO.Recordset
‘ 顧客ID(数値)と開始日(日付)をパラメータとして渡す
‘ クエリ側には [prm_CustomerID] と [prm_StartDate] を定義しておくこと
Set rs = GetSecureRecordset(“Q_受注検索”, Me.txtID, Me.txtDate)
‘ データ処理…
rs.Close
Set rs = Nothing
End Sub
—
現場で差がつく「極限の知見」
1. パラメータの宣言を怠るな
クエリ(`Q_受注検索`など)のSQLデザインビューで、必ず「パラメータ」設定を行うこと。
`PARAMETERS prm_CustomerID Long, prm_StartDate DateTime;`
これを先頭に記述することで、AccessはSQLコンパイル時に型の検証を行う。これが最速の実行速度を保証する。
2. CurrentDbを乱用するな
`CurrentDb` は呼び出すたびに新しいデータベースオブジェクトを生成する。ループ内で何度も `CurrentDb` を叩くのは自殺行為だ。
コードの冒頭で一度 `Dim db As DAO.Database: Set db = CurrentDb` と変数に格納し、そのインスタンスを使い回せ。これはメモリ管理の基本中の基本である。
3. DAO vs ADO
Accessのネイティブな操作であれば、迷わずDAOを選択せよ。ADOはSQL Server等との連携には強力だが、Accessローカルのファイル操作においてDAOの速度に勝るものはない。
—
まとめ:保守性の高いシステムを作るために
コードを書くとき、常に問いかけてほしい。
「このコードは、半年後の自分が仕様変更したときに、副作用なく修正できるか?」
パラメータ化されたクエリ設計は、SQLとロジックを完全に分離する。SQLの調整が必要になっても、VBAのコードを一行もいじらずにクエリ定義を書き換えるだけで済む。
「動的」とは、文字列を継ぎ接ぎすることではない。
「可変要素を安全な枠組みに流し込むこと」を指す。
この設計思想こそが、伝説のエンジニアの第一歩だ。現場のコードを、今日から「安全」で「高速」なものに書き換えろ。健闘を祈る。
