【入門編】動的SQL生成時の「クォーテーション地獄」を回避するスマートな文字列構築法 – Access VBA解析バイブル

スポンサーリンク

こんにちは! Access VBAの開発現場で、画面の入力値をもとにSQLを組み立てていたら、わけが分からなくなって頭を抱えてしまった……そんな経験はありませんか?

「日付の囲みはシャープ `#` だっけ? 文字列はシングルクォーテーション `’` だっけ? いや、入力値の中にシングルクォーテーションが含まれていてエラーになった!」

俗に言う「クォーテーション地獄」です。ここを突破できずに、VBAの動的SQL生成を諦めてしまう人は少なくありません。

でも、安心してください。今回ご紹介する「スマートな文字列構築法(ヘルパー関数)」をマスターすれば、この地獄から永遠に抜け出すことができます。ここをクリアすれば、あなたのAccess VBAスキルは一段上のステージに行けますよ。一緒に優しく紐解いていきましょう!

—

1. なぜ「クォーテーション地獄」が起きるのか?

まずは敵を知ることから始めましょう。
例えば、画面のテキストボックスから「ユーザー名」と「登録日」を受け取って、SQL文を組み立てるとします。

‘ ❌ やりがちな危険なコード
Dim sql As String
sql = “SELECT FROM T_顧客 WHERE 顧客名 = ‘” & Me.txt顧客名 & “‘ AND 登録日 >= #” & Me.txt登録日 & “#”

一見、動くように見えますよね。しかし、これには大きな爆弾が隠されています。

1. データ型ごとの記号の迷宮:文字列は `’`、日付は `#`、数値は記号なし。これを毎回手書きで結合しようとすると、必ずダブルクォーテーション(`”`)の閉じ忘れや、連結演算子(`&`)のつけ忘れが起きます。
2. SQLインジェクション&文字化けの危機:もしユーザーが `O’Connor` のような名前にシングルクォーテーションを含む文字を入力したらどうなるでしょうか? SQLの構文が壊れて容赦なくエラー(実行時エラー)が発生します。

この「記号の管理」と「エスケープ処理」を、毎回SQLを書くたびに手動で行うのは、エンジニアの仕事としてあまりに非効率で、精神的にも良くありません。

—

2. 解決の鍵:型に応じた「専用ヘルパー関数」を作る

プログラミングの鉄則は「面倒な作業は機械(関数)にやらせる」ことです。
SQLに埋め込む直前の値を、データ型に合わせて「安全な形に整形して返す関数(ヘルパー関数)」を一つだけ用意しましょう。

以下のコードを、標準モジュール(例: `modSqlHelper` など)にペスコピしてください。これが、あなたの盾と剣になります。

実装コード:SQL安全整形ヘルパー

Option Compare Database
Option Explicit

‘ ==============================================================================
‘ 処理名 : ToSqlValue
‘ 概要 : 渡された値をSQLのデータ型に合わせて適切にクォーテーション・エスケープ処理する
‘ 引数 : vVal – 評価する値
‘ : dataType – データ型 (dbText, dbDate, dbNumeric など)
‘ 戻り値 : SQL文にそのまま組み込める文字列
‘ ==============================================================================
Public Function ToSqlValue(ByVal vVal As Variant, ByVal dataType As Integer) As String

‘ 1. 未入力(Nullまたは長さ0の文字列)の場合は SQLの NULL を返す
If IsNull(vVal) Or vVal = “” Then
ToSqlValue = “NULL”
Exit Function
End If

Select Case dataType
Case vbString, dbText, dbMemo
‘ 2. 文字列型:シングルクォーテーションで囲む
‘ ※万が一、入力値の中にシングルクォーテーションが含まれていたら「置換(エスケープ)」する!
Dim escapedText As String
escapedText = Replace(CStr(vVal), “‘”, “””)
ToSqlValue = “‘” & escapedText & “‘”

Case vbDate, dbDate
‘ 3. 日付型:Access(Jet/ACE)の仕様に合わせてシャープ(#)で囲む
‘ ※確実な認識のため、YYYY-MM-DD形式にフォーマットして渡すのがプロの技
ToSqlValue = “#” & Format(CDate(vVal), “yyyy/mm/dd hh:nn:ss”) & “#”

Case vbInteger, vbLong, vbDouble, vbCurrency, dbInteger, dbLong, dbCurrency, dbSingle
‘ 4. 数値型:クォーテーションは不要。念のため数値に変換
If IsNumeric(vVal) Then
ToSqlValue = CStr(vVal)
Else
ToSqlValue = “0” ‘ 数値であるべき場所で非数値なら安全に0を返す
End If

Case Else
‘ その他、不明な型は安全のため文字列として扱う
ToSqlValue = “‘” & Replace(CStr(vVal), “‘”, “””) & “‘”

End Select
End Function

—

3. ヘルパー関数を使った「天国のような」動的SQL生成

先ほど作った `ToSqlValue` 関数を使うと、実際のクエリ生成コードがどう変わるか見てみましょう。

実装例:安全かつスマートなQueryDefの利用

Public Sub SearchCustomer()
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim sql As String

Set db = CurrentDb

‘ あらかじめ用意した一時クエリ(あるいは専用のQueryDefオブジェクト)を利用
‘ ※毎回コンパイルエラーや構文エラーに悩まされないよう、骨組みはシンプルに保つ
sql = “SELECT FROM T_顧客 WHERE 1=1 ”

‘ 画面の入力値(テキストボックス等)をヘルパー関数に通して連結する!
‘ これだけで、型に応じた記号の付与も、シングルクォーテーションのエスケープも自動完了します。

‘ 条件1:顧客名(文字列)
If Not IsNull(Me.txt顧客名検索) Then
sql = sql & ” AND 顧客名 LIKE ” & ToSqlValue(“%” & Me.txt顧客名検索 & “%”, vbString)
End If

‘ 条件2:登録日(日付)
If Not IsNull(Me.txt登録日以降) Then
sql = sql & ” AND 登録日 >= ” & ToSqlValue(Me.txt登録日以降, vbDate)
End If

‘ 条件3:顧客ID(数値)
If Not IsNull(Me.txt顧客ID) Then
sql = sql & ” AND 顧客ID = ” & ToSqlValue(Me.txt顧客ID, vbLong)
End If

‘ デバッグ用:イミディエイトウィンドウに完成したSQLを出力して確認してみよう
Debug.Print sql

‘ クエリ定義(QueryDef)にSQLをセットしてフォームのソースに割り当てる
Set qdf = db.QueryDefs(“qryDynamicSearch”)
qdf.SQL = sql
qdf.Close

‘ サブフォームに結果を反映
Me.sfrmResults.Form.RecordSource = “qryDynamicSearch”
Me.sfrmResults.Form.Requery

Set qdf = Nothing
Set db = Nothing

MsgBox “検索が完了しました!”, vbInformation
End Sub

—

4. このコードの何がスゴいのか?(知見の解説)

1. クォーテーションの迷いがゼロになる
SQLを組み立てるときに、`#` や `’` をどこにつけるべきか考える必要が一切なくなりました。「とりあえず `ToSqlValue(値, 型)` に放り込めば、SQLが理解できる正しい形にして返してくれる」という絶対的な安心感が生まれます。
2. 「O’Connor問題(インジェクション・構文エラー)」の完全防衛
文字列型を処理する部分の `Replace(CStr(vVal), “‘”, “””)` に注目してください。SQL文の中でシングルクォーテーションは「文字列の終わり」を意味するため、データの中に含まれているとSQLがぶっ壊れます。これを `”`(二つ重ねることで、SQL側で「文字としてのシングルクォーテーション」と解釈させる)に自動置換することで、エラーを完璧に回避しています。
3. 日付のフォーマット揺れを防ぐ
Accessのバックエンド(Jet/ACE)は、OSの設定や環境によって日付の解釈(MM/DDかDD/MMか)が変わるという厄介な癖を持っています。ヘルパー関数内で `yyyy/mm/dd hh:nn:ss` に強制フォーマットしているため、「環境が変わったら動かなくなった」という悪夢を完全にシャットアウトできます。

—

まとめ

Access VBAにおける動的SQL生成は、コツさえ掴んでしまえば決して恐ろしいものではありません。

  • SQLの中に直接 `”` や `’` や `#` をベタ書きしない。
  • 型に応じた整形は、専用のヘルパー関数(`ToSqlValue`)に丸投げする。

たったこれだけの設計思想を取り入れるだけで、あなたの書くコードは劇的に美しく、保守しやすく、そしてバグに強いものに生まれ変わります。

「ここをクリアすれば、Access VBAの基本はバッチリですよ!」
ぜひ明日からの開発にこのヘルパー関数を組み込んで、快適なVBAライフを満喫してくださいね。チーフアーキテクトの私としても、あなたのレベルアップを心から応援しています!

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