Accessが「太る」最大の戦犯:QueryDef断片化の真実と、それを根絶する設計指針
Access開発において、多くのエンジニアが直面する「ファイル肥大化の呪い」。
「こまめに最適化(最適化と修復)しているのに、なぜか数日でファイルサイズが戻る…」
もしあなたがそんな悩みを抱えているなら、その原因は「QueryDefの乱立と断片化」にある可能性が極めて高い。
今回は、Accessの内部構造を熟知したアーキテクトの視点から、この「目に見えない肥大化」を根絶するための極限のテクニックを伝授する。
—
なぜQueryDefが「負債」になるのか
Accessにおいて、`CurrentDb.CreateQueryDef` で動的にクエリを作成し、処理後に `Delete` する手法は、一見クリーンに見える。だが、Accessのデータベースエンジン(ACE/Jet)は、削除されたオブジェクトのスペースを即座に再利用しないという特性がある。
結果として、システムテーブル(`MSysObjects` 等)には削除されたQueryDefの残骸が断片化して蓄積され、それがファイル肥大化の直接的な引き金となる。特にパラメータークエリを動的生成し、ループ内で破棄を繰り返す実装は、数千回単位の実行でデータベースを「内部崩壊」させる。
解決策:QueryDefを物理的に作らない「パラメーター化」の鉄則
肥大化を防ぐ唯一の解は、「不必要なQueryDefを作成しない」ことだ。
動的SQLを実行したい場合、`QueryDef` オブジェクトをいちいち作成・保存するのではなく、`DAO.Database.Execute` と `DAO.QueryDef` のパラメータバインディングを組み合わせるのが、プロの流儀である。
堅牢な動的SQL実行テンプレート
以下のコードは、QueryDefを保存(Create)せずに、実行時のみメモリ上でパラメータをバインドする、パフォーマンスと安定性を両立させた実装だ。
‘ ———————————————————
‘ 目的: QueryDefを作成せずに安全に動的SQLを実行する
‘ 利点: システムテーブルを汚さないため、ファイルが肥大化しない
‘ ———————————————————
Public Sub ExecuteDynamicSQL(ByVal sqlString As String, ParamArray params() As Variant)
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim i As Integer
Set db = CurrentDb
‘ 一時的なQueryDefを作成(名前を付けないことで内部保存を回避)
Set qdf = db.CreateQueryDef(“”, sqlString)
‘ パラメータの注入
For i = 0 To UBound(params)
qdf.Parameters(i).Value = params(i)
Next i
‘ 実行(トランザクション管理を行う場合はここに記述)
qdf.Execute dbFailOnError
‘ クリーンアップ
qdf.Close
Set qdf = Nothing
Set db = Nothing
End Sub
—
「どうしてもQueryDefが必要な場合」の最適化ルーチン
もし、レポートのソースとしてQueryDefが不可欠な場合や、複雑な結合条件をSQL文字列で構築せざるを得ない場合は、「使い捨てのQueryDefを明示的に削除し、定期的に最適化を走らせる」という運用が必要になる。
以下のプロシージャを定期的に呼び出すことで、隠れた断片化を物理的に叩き出すことが可能だ。
‘ ———————————————————
‘ 特定のプレフィックスを持つ一時クエリを一括削除する
‘ ———————————————————
Public Sub CleanupTempQueries(ByVal prefix As String)
Dim qdf As DAO.QueryDef
For Each qdf In CurrentDb.QueryDefs
‘ “TMP_” で始まるクエリのみを対象に削除
If Left(qdf.Name, Len(prefix)) = prefix Then
DoCmd.DeleteObject acQuery, qdf.Name
End If
Next qdf
‘ 注意:この後に Application.CompactRepair を推奨
End Sub
アーキテクトからの助言:設計の分水嶺
1. 「永続クエリ」と「動的SQL」を峻別せよ:
変更が少ない複雑な結合は、最初から `QueryDef` として保存しておくべきだ。動的SQLは、あくまで「ユーザー入力に基づいた検索」や「バルクインサート」など、完全な動的生成が必要な場合に限定する。
2. 型安全を疎かにするな:
`ParamArray` を使用する際は、引数の型がSQL内の列の型と一致しているか厳密にチェックすること。暗黙の型変換は、インデックスが効かなくなる致命的なパフォーマンス低下を招く。
3. 最適化の自動化は「運用」で解決する:
Accessの `CompactRepair` は強力だが、排他制御が必要だ。アプリケーションの終了時にフラグを立て、次回起動時に「最適化するかどうか」をユーザーに問うか、バックグラウンドジョブとして実行する設計を推奨する。
結論
Accessの肥大化は、ツールそのものの限界ではなく、多くの場合「実装の甘さ」に起因する。
`CreateQueryDef` でファイルを汚し続けるのは、今日で終わりにしよう。メモリ上で完結させる動的バインディングこそが、あなたの作るツールを「数年先まで止まらない、軽快なシステム」へと進化させる鍵となる。
コードのコピペは自由だが、その裏にある「メモリとストレージの呼吸」を意識するエンジニアであれ。それが、真の業務自動化エンジニアへの第一歩だ。
