こんにちは!Access VBAの世界へようこそ。チーフアーキテクトの私です。
マクロの記録ボタンをポチポチ押すだけのステージから抜け出し、「自分の手でシステムをコントロールしたい!」そう思ってVBAの扉を叩いたあなた、素晴らしい着眼点です。ここをクリアすれば、Access VBAの基本はバッチリですよ。自信を持って進んでいきましょう!
さて、今回は実務で避けて通れない「動的なSQLの実行」、そして何よりも大切な「安全性の確保」についてお話しします。
初心者の方によありがちなのが、画面のテキストボックスに入力された値を、そのまま文字としてSQL文に組み込んでしまうやり方です。
例えばこんなコードですね。
‘ 【危険な例】絶対に真似しないでください!
Dim strSQL As String
strSQL = “SELECT FROM 顧客マスタ WHERE 顧客名 = ‘” & Me.txt顧客名.Text & “‘”
CurrentDb.Execute strSQL
一見、動的に動くので便利に見えますが、これでは「SQLインジェクション」というセキュリティホールをドブ川のように垂れ流しているのと同じです。もしユーザーが `’ OR ‘1’=’1` なんて名前を入力したら……想像するだけでも冷や汗ものですね。全レコードが吹っ飛ぶか、不正に覗き見されてしまいます。
そこで登場するのが、DAO.Parameterオブジェクトです。
今回は、安全かつスマートにパラメータをクエリに渡し、爆速で処理するための極意を伝授します!
—
1. なぜ「Parameterオブジェクト」なのか?
SQLインジェクションを防ぐための王道、それが「パラメータクエリ」です。
文字をそのままSQLに埋め込むのではなく、「ここには後から値が入りますよ」という穴(プレースホルダー)をあらかじめ用意しておき、後から安全に値だけを流し込むという仕組みです。
Accessの裏側で動いているデータベースエンジン(ACE/Jet)は、この仕組みを使うことで「これはただの『データ』であって、SQLの『命令』ではないな」と厳密に区別して処理してくれます。だから安全なのです。さらに、型のミスマッチによるエラーも未然に防げるため、まさに一石二鳥のプロフェッショナルな手法と言えます。
—
2. 実践!安全なパラメータクエリの書き方
百聞は一見に如かず。実際に動かせるサンプルコードを見てみましょう。
今回は、フォームに入力された「顧客ID」を元に、安全にデータを抽出してメッセージボックスに顧客名を表示するプロシージャです。
準備するDAOの作法
DAO(Data Access Objects)を使うときは、あらかじめ「クエリ定義(QueryDef)」というオブジェクトをメモリ上にしっかり用意してあげます。
Public Sub GetCustomerNameSecurely()
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim rs As DAO.Recordset
Dim paramID As Long
‘ 1. 現在のデータベースへの参照を取得
Set db = CurrentDb()
On Error GoTo ErrorHandler
‘ 2. パラメータ付きのSQLを持つQueryDefオブジェクトを一時的に作成
‘ 「@ID」または「[?]」がプレースホルダー(パラメータの目印)になります。
Set qdf = db.CreateQueryDef(“”, “SELECT 顧客名 FROM 顧客マスタ WHERE 顧客ID = [入力ID]”)
‘ 3. 画面のフォームから検索したいIDを取得(型安全のためにLong型に変換)
If IsNumeric(Me.txt入力ID.Value) Then
paramID = CLng(Me.txt入力ID.Value)
Else
MsgBox “有効な数値を入力してください。”, vbExclamation, “入力エラー”
GoTo Cleanup
End If
‘ 4. Parametersコレクション経由で型を明示して値を代入!
‘ ここが今日のハイライトです。
qdf.Parameters(“[入力ID]”).Value = paramID
‘ 5. クエリを実行してレコードセットを取得
Set rs = qdf.OpenRecordset(dbOpenSnapshot)
‘ 6. 結果の判定と表示
If Not rs.EOF Then
MsgBox “該当する顧客名は 「 ” & rs!顧客名 & ” 」 です。”, vbInformation, “検索成功”
Else
MsgBox “指定された顧客IDは見つかりませんでした。”, vbExclamation, “結果なし”
End If
Cleanup:
‘ 7. オブジェクトの開放(メモリリークを防ぐプロの作法)
If Not rs Is Nothing Then rs.Close: Set rs = Nothing
If Not qdf Is Nothing Then qdf.Close: Set qdf = Nothing
Set db = Nothing
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical
Resume Cleanup
End Sub
—
3. コードの心臓部を徹底解剖!
先ほどのコードの中で、初心者がつまずきやすいポイントを3つに絞って解説します。
① `db.CreateQueryDef(“”, …)` の第一引数の秘密
`CreateQueryDef` の第一引数に空文字 `””` を指定すると、Accessの「クエリ」タブの一覧には保存されない「一時的なクエリ(名前のないQueryDef)」を作ることができます。
使い捨てのクエリをわざわざオブジェクトコンテナに残さないことで、不要なゴミを増やさず、パフォーマンスにも優しい設計になります。
② `Parametersコレクション` へのアプローチ
SQL文の中で `[入力ID]` と定義したものは、自動的に `qdf.Parameters` コレクションのメンバーとして登録されます。
qdf.Parameters(“[入力ID]”).Value = paramID
このコードによって、Accessが「あ、この `[入力ID]` には、さきほど取得した `paramID` の数値を入れるんだな」と安全に解釈してバインドしてくれます。文字結合(`&`)を一切使っていないため、悪意あるコードが入り込む隙が1ミリもありません。
③ 徹底的な「お片付け(解放)」
プロとアマを分ける境界線、それはオブジェクトの解放に対する執着心です。
`Recordset` や `QueryDef` を開いたら、処理の最後(あるいはエラー時)に必ず `.Close` し、`Set … = Nothing` でメモリからキレイに消し去りましょう。これをサボると、Access特有の「動作がだんだん重くなる現象(メモリリーク)」を引き起こす原因になります。
—
4. 陥りやすいエラーと対策
ここで、実務でよくあるハマりどころを先回りしてご紹介しておきます。
- エラー:「パラメータが見つかりません。」
- 原因: SQL文の中のパラメータ名と、`qdf.Parameters(“…”)` で指定している名前が1文字でも違っています(大文字小文字やカッコの有無など)。
- 対策: SQL内のプレースホルダー名をシンプルに `[prm1]` のように統一し、コピペで指定するとミスが激減します。
- エラー:「型が一致しません。」
- 原因: テキストボックスの空値(Null)や文字列をそのままLong型やDate型のパラメータにぶち込もうとしています。
- 対策: 代入する前に必ず `IsNumeric` や `IsDate` で型のチェック(バリデーション)を行いましょう。
—
おわりに
お疲れ様でした!
今回学んだ「DAO.Parameterオブジェクトを活用した動的SQLの生成」は、セキュアでモダンなAccess開発において必須の教養です。
「ただ動けばいいや」というコードから、「安全で、美しく、メモリ効率まで考え抜かれた」プロのコードへ。一歩を踏み出したあなたなら、もうマクロの記録器にしがみつく必要はありません。
明日からの開発現場で、ぜひこのテクニックをドヤ顔で(心の中でこっそりと)使ってみてくださいね。それでは、また次の極意でお会いしましょう!
