【Accessの神髄】なぜあなたのコードは止まるのか?DAO.Recordsetの「EOF」を完全掌握せよ
現場で「なぜかループが走らない」「レコードがないとエラーになる」という壁にぶつかっていないだろうか。
Access VBAを用いた業務自動化において、`DAO.Recordset`は心臓部だ。しかし、この心臓部の扱いを誤れば、システムは簡単に呼吸を止める。特に、「レコードが1件も存在しない状態」への配慮を欠いたコードは、爆弾を抱えたまま走っているようなものだ。
今回は、伝説的アーキテクトの視点から、DAOループの「正しい作法」と、現場で生き残るための堅牢な実装術を伝授する。
—
1. なぜ「EOF」だけでは不十分なのか
初心者がやりがちな、最も脆弱なパターンがこれだ。
‘ 【危険なコード】初心者がやりがちな実装
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset(“T_受注データ”)
Do While Not rs.EOF ‘ ここで詰まる可能性がある
Debug.Print rs!受注ID
rs.MoveNext
Loop
rs.Close
一見動くように見える。だが、もしテーブルが空だったら?あるいは抽出条件で1件もヒットしなかったら?
「レコードが存在しない」とき、`rs`は生成されるが、その状態は`EOF`かつ`BOF`でもある。この状況で`rs!受注ID`にアクセスしようとすれば、即座に実行時エラーが発生する。
プロの視点: 「データが必ずある」という前提は、システム開発における最大の慢心だ。データは常に「ゼロ」から「無限」の可能性を秘めていると仮定せよ。
—
2. 堅牢なループ処理の「黄金パターン」
現場のプロダクションコードでは、レコードの有無を判定し、空であれば即座に終了させるフローが鉄則だ。`EOF`のみに頼らず、`BOF`と組み合わせて制御するのが最も美しい。
推奨される実装コード
Public Sub ProcessOrders()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Set db = CurrentDb
‘ パフォーマンスを考慮し、必要な列だけを選択する
Set rs = db.OpenRecordset(“SELECT 受注ID, 受注日 FROM T_受注データ WHERE 処理済 = False”, dbOpenSnapshot)
‘ 【重要】レコードの有無を最初に判定する
If rs.EOF Then
Debug.Print “対象レコードは存在しません。”
rs.Close
Exit Sub
End If
‘ ここまで到達すれば、確実にデータは存在する
rs.MoveFirst
Do Until rs.EOF
‘ 処理内容を記述
Debug.Print “処理中: ” & rs!受注ID
rs.MoveNext
Loop
rs.Close
Set rs = Nothing
Set db = Nothing
End Sub
—
3. アーキテクトからの3つの鉄則
コードを「動くもの」から「壊れないもの」に変えるための、現場で使える知見を授ける。
① dbOpenSnapshot の活用
`dbOpenSnapshot`を指定することで、読み取り専用の軽量なレコードセットが作成される。更新が不要な集計や参照処理で、わざわざ重たい`Dynaset`(既定値)を使う理由はない。メモリ消費を抑え、ロックの競合を防ぐことは、マルチユーザー環境での基本だ。
② 明示的なクローズとメモリ解放
`rs.Close`と`Set rs = Nothing`を省略するエンジニアがいるが、これはメモリリークの温床だ。Accessは小規模なアプリであっても、長時間稼働させればオブジェクトの残滓がパフォーマンスを劣化させる。「開いたら閉じる」。この当たり前の規律が、システムの寿命を延ばす。
③ エラーハンドリングの組み込み
上記コードに、必ず `On Error GoTo` を追加すること。データベース連携において「エラーが起きないこと」を祈るのではなく、「エラーが起きた時にどう振る舞うか」を設計するのが、エンジニアの仕事だ。
—
結論:コードは「対話」である
今回紹介したパターンは、Access開発の基礎中の基礎だ。しかし、この基礎を疎かにするエンジニアは、一生「謎のエラー」に追いかけ回されることになる。
システムはあなたが書いたコードの通りにしか動かない。データがない状態を「異常」ではなく「正常なフローの一部」として扱うこと。それが、堅牢な業務自動化ツールを作るための第一歩だ。
さあ、今すぐあなたの既存コードを見直してほしい。そこに「レコードがゼロのとき」の優しさはあるだろうか?
