【実務・中級編】DAO.QueryDefでパラメータクエリをVBAから安全に実行する – Access VBA解析バイブル

スポンサーリンク

なぜ「文字列結合」でSQLを書くのか?――Access VBAにおける「真に堅牢な」クエリ実行術

Access開発の現場で、未だに `strSQL = “SELECT FROM T_User WHERE ID = ” & Me.txtID` のようなコードを書いていないだろうか。

もしそうなら、今すぐその手を止めてほしい。それは「地雷」を埋め込んでいるのと同義だ。文字列結合によるSQL生成は、単なるセキュリティリスク(SQLインジェクション)の問題ではない。コードの可読性を著しく下げ、デバッグを困難にし、データベースエンジンが備える「クエリプランの再利用」という最適化の恩恵を自ら捨て去る行為だからだ。

本稿では、DAOの`QueryDef`オブジェクトを使い、パラメータを分離して安全かつ高速にクエリを実行する、プロフェッショナルな作法を伝授する。

1. なぜ「QueryDef」を使うべきなのか

Accessには、あらかじめ定義されたクエリ(クエリデザインで作成したもの)を操作する`QueryDef`という強力な武器がある。

SQLインジェクションの無効化

パラメータをSQL文字列として埋め込むのではなく、DAOが提供する`Parameters`コレクション経由で渡すことで、入力値は純粋な「データ」として扱われる。悪意ある文字列や、特殊な記号が含まれていても、SQLコマンドとして実行されることはない。

パフォーマンスの最適化

Access(ACEエンジン)は、一度実行されたパラメータクエリの実行計画をキャッシュする。毎回文字列を生成して実行すると、エンジンは毎回そのSQLをパース(解釈)し直すことになるが、`QueryDef`を使えばそのオーバーヘッドを最小化できる。

2. 実装のベストプラクティス:プロダクションコード

以下は、実務でそのまま利用可能な、堅牢性を追求した実装パターンだ。

Public Sub ExecuteSafeQuery(ByVal targetID As Long, ByVal userName As String)
Dim db As DAO.Database
Dim qdf As DAO.QueryDef

‘ データベースオブジェクトの取得はCurrentDb関数で行う
‘ ローカル変数に格納することで、パフォーマンスとメモリ管理の安定を図る
Set db = CurrentDb

‘ クエリ定義を取得(事前にAccessのクエリデザインで作成しておく)
‘ qdf_UpdateUser: UPDATE T_User SET Name = [p_Name] WHERE ID = [p_ID]
Set qdf = db.QueryDefs(“qdf_UpdateUser”)

On Error GoTo ErrorHandler

‘ パラメータの明示的な設定
‘ 文字列結合を一切行わず、オブジェクトモデル経由で値を渡す
qdf.Parameters(“p_ID”).Value = targetID
qdf.Parameters(“p_Name”).Value = userName

‘ クエリの実行
qdf.Execute dbFailOnError

Debug.Print “クエリ実行成功: ” & qdf.RecordsAffected & ” 件更新”

CleanExit:
‘ オブジェクトの解放はメモリ管理の鉄則
If Not qdf Is Nothing Then qdf.Close: Set qdf = Nothing
Set db = Nothing
Exit Sub

ErrorHandler:
MsgBox “データベースエラーが発生しました: ” & Err.Description, vbCritical
Resume CleanExit
End Sub

このコードの「賢い」ポイント

1. `dbFailOnError` オプション: これを忘れてはならない。指定しない場合、実行中にエラーが発生してもVBAはそれを無視して次の行へ進んでしまう。データベースの整合性を守るための必須オプションだ。
2. `RecordsAffected`の確認: 期待通りの件数が更新されたかを確認する癖をつけよう。これはバグの早期発見に直結する。
3. オブジェクトのクローズ: `qdf.Close`を明示的に呼び出すことで、リソースを即座に解放する。小規模アプリなら無視できるかもしれないが、多人数で共有するデータベースでは、こうした「行儀の良さ」がアプリの寿命を決める。

3. さらに一段上の実装を目指すなら

もし、実行時にSQLを動的に組み立てなければならない複雑な要件がある場合は、`db.CreateQueryDef(“”, strSQL)` を使って「一時クエリ」を作成し、上記と同様にパラメータを流し込む手法がある。

‘ 一時的なQueryDefを作成して実行する例
Set qdf = db.CreateQueryDef(“”, “SELECT FROM T_User WHERE ID = [p_ID]”)
qdf.Parameters(“p_ID”).Value = 101
Set rs = qdf.OpenRecordset()

この手法を使えば、クエリ定義を汚さずに、かつ安全にSQLを構築できる。

最後に:エンジニアとしての矜持

「動けばいい」というコードは、一ヶ月後の自分にとっての負債になる。
今回紹介した`QueryDef`によるパラメータ化は、セキュリティ、パフォーマンス、保守性のすべてにおいて、文字列結合によるSQL生成を圧倒する。

Accessは古い技術だと言われることもある。しかし、その中核にあるDAOのオブジェクトモデルを正しく理解し、使いこなすエンジニアは極めて少ない。「ツールを使いこなす」のではなく「ツールを掌握する」意識を持てば、Accessは依然として業務自動化における最強の武器であり続けるだろう。

さあ、あなたのコードから文字列結合を駆逐し、真にプロフェッショナルな基盤を構築してほしい。

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