Access VBAの深淵へ:動的SQLで「NULL」という怪物に立ち向かう極意
こんにちは。現場で泥臭いシステム改修から、大規模な業務自動化設計までを手掛けているエンジニアです。
Access VBAを使っていて、「クエリに値を渡しているのに、なぜかNULLのデータだけが拾えない」あるいは「エラーで止まってしまう」という経験はありませんか?
これは、「NULLは値ではなく『状態』である」というデータベースの哲学を理解しているかどうかの分かれ道です。今日は、動的SQLを生成する際に誰もが一度は足を取られる「IS NULL判定の罠」を、伝説的な知見を持って解き明かします。
—
1. なぜ「NULL」は計算式を破壊するのか?
SQLの世界において、`NULL`は「空っぽ」ではなく「不明(Unknown)」を意味します。
例えば、`WHERE 担当者ID = NULL` と書いても、データベースは「TRUE」を返しません。なぜなら、「担当者IDが不明なものはどれか?」という問いに対し、そもそも比較が成立しないからです。
そのため、プログラミング初心者が陥りやすいのが、「値があるときはイコール、ないときはNULL」という分岐をSQLの中で強引に行おうとするミスです。
—
2. 「動的SQL生成」の鉄則:NULL分岐をコードで解決せよ
クエリ定義(QueryDef)を使ってSQLを組み立てる際、パラメータがNULLかどうかをSQL文の中で判定させようとするのは非効率です。SQL生成の段階(VBA側)で、SQLの構造そのものを切り替えるのが、プロのエンジニアの流儀です。
【悪い例】SQL内で無理やりNULL判定(動くが、パフォーマンスが悪く管理不能)
`WHERE (担当者ID = [prmID] OR [prmID] IS NULL)`
※これでも動きますが、複雑な条件が重なるとインデックスが効かなくなり、データ量が増えた瞬間にシステムが悲鳴を上げます。
【良い例】VBA側でSQLを組み立てる
パラメータがNULLなら「IS NULL」という文字列をSQLに埋め込み、値があるなら「= ‘値’」を埋め込む。これが正攻法です。
Public Sub GenerateDynamicSQL(Optional prmID As Variant)
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim strSQL As String
Dim strWhere As String
Set db = CurrentDb
‘ 1. 基本的なSQLのベースを作成
strSQL = “SELECT FROM T_売上明細”
‘ 2. NULL判定による分岐処理(ここが生命線!)
If IsNull(prmID) Then
‘ 値が渡されていない場合は「NULLであるデータ」を探す条件に差し替える
strWhere = ” WHERE 担当者ID IS NULL”
Else
‘ 値がある場合は安全に文字列として結合
strWhere = ” WHERE 担当者ID = ” & prmID
End If
‘ 3. クエリ定義の更新(QueryDefのライフサイクル管理)
‘ 既存のクエリを上書きして実行準備を整える
On Error Resume Next
db.QueryDefs.Delete “qry_DynamicOutput”
On Error GoTo 0
Set qdf = db.CreateQueryDef(“qry_DynamicOutput”, strSQL & strWhere)
‘ ここでクエリを実行、またはフォームのレコードソースに設定する
Debug.Print “生成されたSQL: ” & qdf.SQL
‘ 後処理
Set qdf = Nothing
Set db = Nothing
End Sub
—
3. プロが教える「陥りやすい罠」と対策
このコードを書く際、初心者が必ずと言っていいほどハマるポイントが3つあります。
① 空文字(””)とNULLの混同
Accessでは、フォームのテキストボックスを空にしてEnterを押すと、NULLではなく「長さゼロの文字列(””)」が入ることがあります。
- 対策: `If Len(Nz(prmID, “”)) = 0 Then` と記述することで、NULLと空文字を両方とも「値なし」としてハンドリングするのが現場の定石です。
② SQLインジェクションへの配慮
もし画面から入力された値をそのままSQLに埋め込む場合、悪意のあるユーザーが `’ OR ‘1’=’1` などと入力すると、データが全抽出されてしまいます。
- 対策: 可能な限り `QueryDef` の `Parameters` コレクションを使用し、値を「プレースホルダー」として渡す設計にシフトしましょう。
③ QueryDefの削除忘れ
`CreateQueryDef`を繰り返すと、Accessの内部にゴミクエリが大量に蓄積され、ファイルサイズが肥大化します。
- 対策: 処理の冒頭で必ず `Delete` を行うか、あるいは `TempQueryDef` のような設計(一時的に作成して即座に破棄する仕組み)を徹底してください。
—
最後に:ここをクリアすれば、あなたはもう脱・初学者!
今回紹介した「NULL判定の分岐」をVBAで制御できるようになれば、Accessのクエリ開発は格段に安定します。
「動的にSQLを作る」ということは、「プログラムにデータベースの構造を組み立てさせる」という、非常に強力な権限を行使することです。だからこそ、NULLのような曖昧な値を丁寧に扱うことが、システムの堅牢性に直結します。
まずは上記のコードをコピーして、あなたのプロジェクトの検索機能に組み込んでみてください。もしエラーが出たら、それは「システムがあなたに何かを教えようとしているサイン」です。一つひとつ紐解いていけば、必ず最高峰のエンジニアへと近づけますよ。
応援しています。困ったときは、またいつでも相談してくださいね。
