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

スポンサーリンク

こんにちは!Access VBAの世界へようこそ。
マクロの記録から一歩踏み出し、「自分の手で自由自在にシステムを操りたい」と願うあなたへ。今日はいよいよ、プロのエンジニアも必ず使っている「安全で美しいデータベース操作の極意」をお伝えします。

ここをクリアすれば、あなたの書くVBAコードの安全性とパフォーマンスは劇的に跳ね上がります。ぜひ最後までついてきてくださいね!

なぜ文字列結合のSQLは「悪魔の所業」なのか?

Access VBAでデータベースを操作する時、初学者が最初にやりがちなのが、こんなコードです。

‘ 【危険なアンチパターン】絶対に真似しないでください!
Dim sql As String
Dim inputName As String
inputName = Me.txtSearch.Value ‘ 画面のテキストボックスから値を取得

sql = “SELECT FROM T_顧客 WHERE 顧客名 = ‘” & inputName & “‘”
CurrentDb.Execute sql ‘ エラーや不正アクセスの温床!

一見、何の問題もないように見えますよね? 動かすだけなら動きます。しかし、この書き方にはエンジニアの世界で「3大罪」と呼ばれる致命的な欠点があります。

1. シングルクォーテーションの呪い
もしユーザーが入力した名前に `O’Connor` のようなアポストロフィが含まれていた場合、SQLの構文が破壊されて容赦なくエラー(実行時エラー)が発生します。
2. パフォーマンスの無駄遣い
Access(Jet/ACEエンジン)は、SQL文が来るたびに「このクエリをどうやって実行するのが最善か?」を毎回ゼロから考え直します(実行計画の再コンパイル)。これでは重い処理でアプリがフリーズ気味になります。
3. SQLインジェクションの恐怖
悪意あるユーザーが入力欄に特殊なSQL文を仕込んだ場合、意図しないデータの改ざんや破壊が行われる危険性があります。

「じゃあ、安全かつ高速にするにはどうすればいいの?」
その答えが、今回主役として登場する `DAO.QueryDef` オブジェクト です。

DAO.QueryDef とパラメータクエリの正体

`QueryDef`(クエリデフィニション)とは、一言で言うと「Accessのクエリデザイン画面にあらかじめ保存されているSQLの設計図」をVBAから直接操るためのオブジェクトです。

あらかじめAccess側に「ここに値が入るよ」という「パラメータ(穴)」を用意しておき、VBA側からその穴にピタッとハマる値を安全に流し込む。この仕組みをパラメータクエリと呼びます。

図解するなら、こういうイメージです。

[ Accessのクエリ ]
SELECT FROM T_顧客 WHERE 顧客名 = [引数_顧客名] ← パラメータ(穴)を用意!

│ 実行!
[ VBA (QueryDef) ]
1. クエリを呼び出す
2. .Parameters(“[引数_顧客名]”) = “渡したい値” ← 安全に値をセット!
3. .OpenRecordset() で高速取得

これなら、文字列の結合なんて汚い真似をする必要は一切ありません。Accessがパラメータの型をしっかり解釈してくれるため、記号のエラーも、セキュリティのリスクも、一網打尽に解決できます。

【実践】QueryDefで安全にパラメータを渡すコード

それでは、実際に開発現場でそのまま使える実用的なコードを見ていきましょう。

今回は、Accessのクエリウィンドウに `qry_顧客検索` という名前のパラメータクエリがすでに保存されている前提で解説します。

事前準備:クエリのSQLはどうなっているか?

AccessのクエリデザイナでSQLビューを開き、以下のように記述しておきます。

PARAMETERS [引数_顧客名] Text ( 255 );
SELECT FROM T_顧客 WHERE 顧客名 LIKE [引数_顧客名] & “”;

(※ `PARAMETERS` 宣言を書いておくと、Accessがデータの型を厳密にチェックしてくれます)

VBAのコーディング

ボタンクリックイベントなどに、以下のように記述します。

Sub SearchCustomerSecurely()
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim rs As DAO.Recordset
Dim searchName As String

On Error GoTo ErrorHandler

‘ 1. 画面の入力コントロールから値を取得
searchName = Nz(Me.txtSearchName.Value, “”)

If searchName = “” {
MsgBox “検索する顧客名を入力してください。”, vbExclamation, “入力エラー”
Exit Sub
}

‘ 2. 現在のデータベースを参照
Set db = CurrentDb

‘ 3. あらかじめ保存されているQueryDefオブジェクトを取得
‘ ※ここで新規作成するのではなく、既存のクエリを呼び出すのがポイント!
Set qdf = db.QueryDefs(“qry_顧客検索”)

‘ 4. 【最重要】パラメータに安全に値を代入する
‘ 文字列結合はせず、オブジェクトのプロパティとして値を渡す
qdf.Parameters(“[引数_顧客名]”) = searchName

‘ 5. クエリを実行し、結果をレコードセットとして受け取る
Set rs = qdf.OpenRecordset(dbOpenSnapshot)

‘ 6. 結果の判定と処理
If rs.EOF Then
MsgBox “該当する顧客は見つかりませんでした。”, vbInformation, “検索結果”
Else
‘ レコードが存在する場合の処理(例:イミディエイトウィンドウに表示)
Do Until rs.EOF
Debug.Print “顧客ID: ” & rs(“顧客ID”) & ” / 顧客名: ” & rs(“顧客名”)
rs.MoveNext
Loop
MsgBox “検索が完了しました。イミディエイトウィンドウを確認してください。”, vbInformation, “成功”
End If

CleanUp:
‘ 7. オブジェクトの解放(メモリリークを防ぐプロの作法)
If Not rs Is Nothing Then rs.Close: Set rs = Nothing
If Not qdf Is Nothing Then Set qdf = Nothing
Set db = Nothing
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “エラー”
Resume CleanUp
End Sub

コードの注目ポイント(プロの技)

ここまでのコードで、初学者がステップアップするための重要なポイントがいくつか詰まっています。

1. `Nz()` 関数でヌル(Null)対策を完璧に

`searchName = Nz(Me.txtSearchName.Value, “”)` の部分です。もしテキストボックスが空欄のまま実行された場合、VBAでは `Null` が返されます。これをそのまま渡すと予期せぬエラーになるため、`Nz` 関数で強制的に長さ0の文字列 `””` に変換しています。

2. `dbOpenSnapshot` でパフォーマンスを極限まで高める

`qdf.OpenRecordset(dbOpenSnapshot)` と書いている点に注目してください。
データを「見るだけ(参照のみ)」の場合、編集可能な通常レコードセット(`dbOpenDynaset` 等)を開くのはメモリの無駄遣いであり、処理速度も落ちます。スナップショットを指定することで、読み取り専用の軽量なキャッシュメモリ空間で高速にデータを取得できます。

3. 解放処理(CleanUp)の徹底

オブジェクト変数を使い終わったら、必ず `Set rs = Nothing` のようにメモリから解放してあげましょう。これをサボると、Accessがメモリを食いつぶし、動作が不安定になる原因(メモリリーク)になります。

まとめ:ここをクリアすれば、Access VBAの基本はバッチリです!

お疲れ様でした!今回は `DAO.QueryDef` を使った安全かつ高速なパラメータクエリの実行方法を解説しました。

  • SQLの文字列結合はエラーと脆弱性の元なので絶対にやらない
  • クエリはAccess側に保存し、VBAからは `QueryDef` で呼び出す
  • パラメータは `.Parameters(“名前”)` を通して安全にセットする

この作法を身につけたあなたは、もう「マクロの記録から脱却した初級者」ではありません。実務で通用する、堅牢で美しいコードを書くエンジニアの仲間入りです。

ぜひ、あなたの身の回りのAccessアプリの検索処理にも取り入れてみてくださいね。それでは、次のステップでお会いしましょう!

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