Accessの「死の淵」を歩く:DAO.RecordsetにおけるEOFの真実と、メモリを掌握する作法
Accessという環境は、長年現場を支え続けてきた不沈艦であると同時に、扱いを誤れば即座にシステムを沈没させる不安定さを孕んでいる。
特に、DAO(Data Access Objects)を用いたレコードセットの操作は、VBAにおける「呼吸」のようなものだ。しかし、多くのエンジニアがこの「呼吸」の作法を誤り、メモリリークや意図せぬエラーをシステムに蓄積させている。
今回は、最も基本でありながら、最も軽視されがちな「EOF(End Of File)」判定の深淵を紐解く。
—
なぜ「ただのIF EOF」では足りないのか
初心者は往々にして、レコードセットを開いた直後に `If Not rs.EOF Then` と記述して安堵する。だが、これは「レコードが存在しない」という例外ケースに対する防衛としては不十分だ。
厳密なシステム設計においては、以下の二点を見落としてはならない。
1. BOF(Beginning Of File)の存在: レコードセットが空の場合、`EOF`と`BOF`は同時に`True`を返す。
2. カーソルの位置: レコードセットを開いた直後のカーソル位置は、最初のレコードの「前」にある。
単に `EOF` だけを見てループを回すと、空のデータセットをハンドリングする際に論理的な不整合が生まれるリスクがある。プロフェッショナルは、常に「空」という状態を明確に定義して制御する。
—
極限のレコードセット制御パターン
以下は、メモリを解放し、例外を完全に封じ込めるための「標準実装パターン」である。これをテンプレートとして叩き込め。
Public Sub SafeRecordsetProcessing()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim strSQL As String
‘ オブジェクトの初期化
Set db = CurrentDb
strSQL = “SELECT FROM T_Master WHERE Status = ‘Active'”
‘ ロックを最小限にするため、必要に応じたタイプとオプションを指定
‘ 巨大なデータセットを扱う場合は dbOpenSnapshot を推奨
Set rs = db.OpenRecordset(strSQL, dbOpenSnapshot)
‘ 【重要】レコードが存在しない場合のガード節
‘ BOFとEOFの両方をチェックすることで、空のレコードセットを確実に除外
If rs.EOF And rs.BOF Then
Debug.Print “対象データは存在しません。”
GoTo Cleanup
End If
‘ ループ処理
Do Until rs.EOF
‘ ここで業務ロジックを実行
Debug.Print rs!ID & “: ” & rs!Name
rs.MoveNext
Loop
Cleanup:
‘ 【重要】リソースの解放
‘ VBAのガベージコレクションを待つな。明示的なNothing代入が正義である
If Not rs Is Nothing Then
rs.Close
Set rs = Nothing
End If
Set db = Nothing
End Sub
—
シニアエンジニアが意識すべき「メモリの重み」
このコードを見て「`Set rs = Nothing` は自動的に行われるのでは?」と疑問に思う者は、Accessのオブジェクトモデルを過信している。
Accessの内部エンジン(ACE/Jet)は、オブジェクトがスコープを抜けても、直ちにメモリからパージされるとは限らない。特に、大量の処理を繰り返すバッチ処理や、バックエンドとして稼働するAccessシステムにおいて、この「明示的な解放」を怠ることは、数日後にアプリケーションがクラッシュする時限爆弾を仕掛けているのと同じだ。
現場での知見:API連携時の最適化
API経由で外部システムとデータをやり取りする際、AccessのRecordsetは中継地点となる。ここで `dbOpenSnapshot` を多用し、メモリ上に結果をキャッシュさせる設計を行うと、ネットワーク越しの遅延を最小限に抑えられる。逆に、頻繁に値を更新する場合は `dbOpenDynaset` を選択し、トランザクション境界を厳格に管理する。
「とりあえず動く」コードから、「なぜこのコードが堅牢なのかを説明できる」コードへ。
Access VBAはレガシーな言語ではない。使い手次第で、それは極めて高度なデータ処理プラットフォームへと変貌する。EOFの判定一つに、エンジニアの美学とシステムへの敬意を込めること。それが、真のアーキテクトへの第一歩だ。
