【Access VBA】QueryDefの深淵:動的SQLにおけるNULLの呪縛と「解」
Access開発において、`QueryDef`を使いこなせているか否かで、そのエンジニアの格が決まる。
多くの者は`DoCmd.RunSQL`や、野良の文字列連結でSQLを生成しては、実行時に「型不一致」や「予期せぬNULLの脱落」で夜を明かす。だが、真にシステムを支配する者は、「SQL生成のライフサイクル」と「Jet/ACEエンジンの評価ロジック」を完璧に制御下に置いている。
今回は、動的SQLにおいて誰もが一度は地獄を見る「NULLの扱い」に焦点を当て、堅牢なアーキテクチャへの昇華を目指す。
—
1. なぜ「NULL」は動的SQLを破壊するのか
SQLにおける`= NULL`は常に`Unknown`を返し、フィルタリングから除外される。これは基礎中の基礎だ。しかし、動的SQLを生成するロジックにおいて、パラメータがNULLであるか否かを、文字列連結の段階で「SQL構文としてどう解釈させるか」を設計していないコードが多すぎる。
例えば、`WHERE field = ` & varValue と書いたとき、`varValue`がNULLならSQLは崩壊する。これを防ぐために、我々は動的なバリデーションと、SQL構文の切り替えを実装せねばならない。
—
2. 実装の極致:QueryDefとパラメータ管理の定石
オブジェクトの生成と破棄を疎かにする者は、メモリリークという名の「時限爆弾」を抱えていることになる。`QueryDef`を生成したら、必ず明示的に破棄せよ。そして、動的SQLの構築には、文字列連結ではなく`Parameters`コレクションを介した「型安全なパラメータ化」を推奨する。
堅牢な動的SQL実行のテンプレート
Public Sub ExecuteDynamicQuery(ByVal targetParam As Variant)
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim strSQL As String
Set db = CurrentDb
‘ 1. SQLの雛形を動的に決定する(IS NULL判定の分岐)
If IsNull(targetParam) Then
strSQL = “SELECT FROM T_Master WHERE TargetField IS NULL”
Else
strSQL = “SELECT FROM T_Master WHERE TargetField = [prmValue]”
End If
‘ 2. オブジェクト生成(既存の名前があれば削除して再作成)
On Error Resume Next
db.QueryDefs.Delete “tmp_Query”
On Error GoTo 0
Set qdf = db.CreateQueryDef(“tmp_Query”, strSQL)
‘ 3. パラメータの注入(NULLでない場合のみ)
If Not IsNull(targetParam) Then
qdf.Parameters(“prmValue”).Value = targetParam
End If
‘ 4. 実行またはレコードセット取得
Dim rs As DAO.Recordset
Set rs = qdf.OpenRecordset(dbOpenSnapshot)
‘ … ここで処理 …
‘ 5. 明示的なクリーンアップ(極めて重要)
rs.Close
Set rs = Nothing
qdf.Close
Set qdf = Nothing
Set db = Nothing
End Sub
—
3. レガシーシステムにおけるメモリ最適化の真髄
Access VBAのガベージコレクションを信用してはならない。特に、大規模なレコードセットを扱う際、`Recordset`や`QueryDef`の解放忘れは、システムの応答速度を確実に劣化させる。
- Set Nothingの徹底: スコープを抜ける前に、必ず参照を解放する。これは単なるマナーではなく、Jetエンジン(ACE)のロックファイルを適切に閉じるための「儀式」である。
- 名前付きQueryDefの再利用: 頻繁に実行するクエリであれば、`CreateQueryDef`を繰り返すのではなく、永続的な`QueryDef`の`SQL`プロパティを書き換える手法を取るべきだ。ただし、マルチユーザー環境での競合には細心の注意が必要となる。
—
4. 伝説のエンジニアに求められる「もう一段上の視点」
システム間連携において、外部APIや他DBとデータを同期する際、このNULL判定ロジックはより複雑化する。
- NULLと空文字の境界: VBAの`Null`と、データベース上の`””`(空文字)は別物だ。`Nz()`関数で安易に変換せず、ビジネスロジック上で「未設定」と「空」を明確に区別してSQLへ渡すことが、バグを未然に防ぐ唯一の道である。
- Windows APIの介入: 極限のチューニングを求めるなら、`GetTickCount`等を用いてクエリの実行時間を計測し、ログ出力する機能をラップしておくべきだ。ボトルネックは常に「クエリの実行計画」にある。
結びとして
コードは「動けばいい」ものではない。「将来、誰がメンテナンスしてもバグを生ませない構造」こそが、シニアエンジニアが担保すべき品質だ。`QueryDef`を操り、NULLという不確定要素を飼い慣らすこと。それが、Accessというレガシーの海を渡り切るための、唯一の技術的回答である。
さあ、今すぐ自身のコードを見直せ。そこにはまだ、君の手で最適化されるべき「未解決のNULL」が眠っているはずだ。
