Accessが「肥大化」する真因:QueryDefの断片化を制するアーキテクトの矜持
Access開発において、多くのエンジニアが犯す最大の過ちの一つが「動的クエリの安易な生成と破棄」だ。
「レコードセットを抽出するために、SQLを組み替えては`QueryDefs.Delete`を呼ぶ」。このルーチンワークこそが、AccessのバックエンドであるACE(Jet)エンジンを蝕み、ファイルサイズを肥大化させる諸悪の根源である。今日は、この「断片化」という見えない恐怖と、それを無力化するための極限の最適化戦略について語る。
なぜQueryDefがファイルを破壊するのか
Accessにおいて`QueryDef`オブジェクトは単なるテキストの器ではない。作成されたクエリは、JETエンジンによって解析され、実行計画(Query Plan)として内部テーブルに書き込まれる。
問題は、`QueryDefs.Delete`を実行しても、内部的なレコード領域が即座に再利用されない点にある。削除を繰り返せば繰り返すほど、mdb/accdbファイル内には「穴(未使用領域)」が空き続け、ファイルサイズが物理的に膨らむ。これが「コンパクト修復」を頻発させねばならない最大の理由だ。
アーキテクトの解:パラメータークエリの固定化
動的SQLを毎回文字列連結で構築するのは、20世紀の遺物だ。我々が取るべき最適解は、「不変のテンプレートQueryDef」と「パラメーターのバインド」である。
以下のコードは、QueryDefを使い捨てず、物理的なオブジェクトを再利用しながらメモリ効率を最大化する設計思想に基づいている。
実践:QueryDef再利用のための最適化スクリプト
‘ @description: QueryDefを再利用し、断片化を抑制するDAO実装
‘ @note: 毎回Deleteせず、既存オブジェクトが存在すればParametersを更新して使う
Public Sub ExecuteOptimizedQuery(queryName As String, sqlTemplate As String, paramDict As Object)
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim prm As DAO.Parameter
Set db = CurrentDb
‘ 1. QueryDefの存在確認と動的生成の回避
On Error Resume Next
Set qdf = db.QueryDefs(queryName)
If Err.Number <> 0 Then
‘ 初回のみ作成。以後は再利用する
Set qdf = db.CreateQueryDef(queryName, sqlTemplate)
Else
‘ 既存のSQLを上書き(ただし構造は変えない)
qdf.SQL = sqlTemplate
End If
On Error GoTo 0
‘ 2. パラメーターのバインド(文字列連結によるSQL注入と断片化を同時に防ぐ)
For Each prm In qdf.Parameters
If paramDict.Exists(prm.Name) Then
prm.Value = paramDict(prm.Name)
End If
Next
‘ 3. 実行(レコードセット取得などの処理をここに記述)
‘ qdf.Execute dbFailOnError
‘ 4. 明示的解放(オブジェクトのライフサイクルを制御する)
Set qdf = Nothing
Set db = Nothing
End Sub
メモリ最適化の極致:Windows APIの活用
さらに踏み込むなら、長時間稼働するAccessシステムでは、Access内部のメモリ解放が追いつかない場面がある。`CompactRepair`をプログラムから自動実行する場合、単に`DBEngine.CompactDatabase`を呼ぶだけでは不十分だ。
Windows APIを叩き、プロセス全体のワーキングセットをトリミング(Trim)することで、OS側にメモリを強制返還させる。これが、限界までシステムを使い倒す者の流儀である。
If VBA7 Then
Private Declare PtrSafe Function SetProcessWorkingSetSize Lib “kernel32” _
(ByVal hProcess As LongPtr, ByVal dwMinimumWorkingSetSize As LongPtr, _
ByVal dwMaximumWorkingSetSize As LongPtr) As Long
Private Declare PtrSafe Function GetCurrentProcess Lib “kernel32” () As LongPtr
End If
‘ メモリを解放し、OSにリソースを返す
Public Sub OptimizeMemoryUsage()
Dim hProcess As LongPtr
hProcess = GetCurrentProcess()
‘ メモリ制限を最小化し、不要なページングを強制排除
SetProcessWorkingSetSize hProcess, -1, -1
End Sub
結論:保守とは「破壊」を最小化すること
QueryDefを削除するたびに、データベースは傷つく。我々が書くコードは、単に動けばいいというものではない。「10年後もファイルサイズが変わらず、エラーを吐かないコード」こそが、最高峰のエンジニアが追求すべき価値だ。
- Deleteを禁ずる: クエリは更新せよ。生成するな。
- Parametersを活用せよ: 文字列連結によるSQLビルドは、脆弱性と肥大化を生む。
- ライフサイクルを管理せよ: オブジェクトは必ず`Nothing`に倒し、Windowsのリソースにも責任を持て。
この規律をシステムに組み込んだ時、あなたの管理するAccessシステムは、気まぐれな玩具から、真に堅牢なエンタープライズ・アーキテクチャへと進化を遂げるはずだ。
