【実務・中級編】動的SQLにおける「NULL値」の扱いとIS NULL判定の罠 – Access VBA解析バイブル

スポンサーリンク

Access VBAの深淵:QueryDefにおける「NULLの罠」と動的SQLの最適解

Access開発の現場で、多くのエンジニアが「なぜか検索結果が正しく返ってこない」「データがあるはずなのにヒットしない」という事象に遭遇する。その犯人の9割は、動的SQLにおける「NULL値のハンドリング」の甘さだ。

今回は、QueryDefを駆使し、NULLを適切に御する堅牢なアーキテクチャの構築法を伝授する。小手先の条件分岐でコードを汚すのは今日で終わりにしてほしい。

—

1. なぜ「WHERE 句 = NULL」は破滅を招くのか

SQLにおいて `NULL` は「値」ではない。「値が存在しないという状態」だ。したがって、`WHERE column = NULL` と書いても、SQLは永遠に真を返さない。

動的SQLでパラメータを結合する際、安易に `WHERE col = ‘ & Nz(myParam, “NULL”) & ‘` のようなコードを書くと、`col = NULL` という構文が生成され、意図通りに動かない。正しくは `IS NULL` 演算子を使うべきなのだが、これを動的SQLの中で条件分岐させると、コードがスパゲッティ化するのは自明の理だ。

—

2. 堅牢な設計指針:ロジックの分離

保守性を高める唯一の解は、「SQLテンプレートの構築」と「パラメータのバインド」を明確に分離することだ。

QueryDefオブジェクトを活用し、生のSQL文字列を毎回組み立てるのではなく、パラメータオブジェクトを介して値を渡す設計を採用する。これにより、NULLの判定ロジックをSQLの構文解析から切り離し、VBA側の関数としてカプセル化できる。

—

3. 実装:プロフェッショナルな動的SQL実行コード

以下は、NULLの扱いを完全に抽象化したプロダクションコードのテンプレートだ。これをモジュール化して活用してほしい。

‘ —————————————————————————
‘ 概要: 堅牢な動的パラメータクエリの実行
‘ 備考: SQL文字列を直接結合せず、QueryDefのParametersコレクションを利用する
‘ —————————————————————————
Public Sub ExecuteRobustQuery(ByVal targetField As String, ByVal filterValue As Variant)
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim strSQL As String

Set db = CurrentDb

‘ 1. SQLテンプレートの定義(IS NULL対応のトリック)
‘ SQLの「WHERE (field = p1 OR (p1 IS NULL AND field IS NULL))」という定石を使う
strSQL = “SELECT FROM T_Master WHERE ” & _
“([target] = [p1] OR ([p1] IS NULL AND [target] IS NULL));”

‘ SQL文字列を置換(本番環境では定数や外部ファイルから読み込む)
strSQL = Replace(strSQL, “[target]”, targetField)

Set qdf = db.CreateQueryDef(“”, strSQL)

‘ 2. パラメータバインド
‘ NULLが渡されてもDAOが正しく解釈するよう明示的に設定
If IsNull(filterValue) Then
qdf.Parameters(“[p1]”).Value = Null
Else
qdf.Parameters(“[p1]”).Value = filterValue
End If

‘ 3. 実行(レコードセットの取得)
Dim rs As DAO.Recordset
Set rs = qdf.OpenRecordset(dbOpenSnapshot)

‘ ここで結果を処理…

rs.Close
Set rs = Nothing
Set qdf = Nothing
Set db = Nothing
End Sub

—

4. この設計が「プロ」である理由

① SQLインジェクション耐性

文字列連結でSQLを生成する場合、ユーザー入力を直接結合すればインジェクションの脆弱性が生じる。QueryDefの `Parameters` を利用することで、DAOが適切に値をエスケープし、セキュリティレベルを一段引き上げている。

② NULLの取り扱いが論理的

`WHERE (col = p1 OR (p1 IS NULL AND col IS NULL))` というパターンは、SQL最適化の観点でも非常に優秀だ。パラメータが値を持つ時は等価判定を行い、NULLであれば `IS NULL` 判定に切り替わる。VBA側で複雑な `If…Then` でSQL文字列を書き換える必要はもうない。

③ 再利用性の高さ

クエリのテンプレートを別関数から呼び出すように設計すれば、フィールド名が動的に変わる検索画面でも同一のロジックを使い回せる。

—

結び:エンジニアへの提言

Accessはレガシーと言われることもあるが、DAOとQueryDefを極めれば、現代のWebフロントエンド以上に高速かつ堅牢なデータ検索基盤を作ることが可能だ。

「とりあえず動くコード」を書くことは、将来の自分への借金に過ぎない。NULLという「境界条件」をいかに美しくハンドリングするか。そこに、君がただのプログラマーではなく、エンジニアであるかどうかの境界線がある。

さあ、今すぐプロジェクトのコードを開き、その場しのぎの文字列連結を、この設計で置き換えてみてほしい。結果は、実行速度とデバッグの容易さという形で必ず返ってくるはずだ。

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