こんにちは!Access VBAの世界へようこそ。
マクロの記録ボタンを押すだけの世界から一歩踏み出し、「自分の手でシステムをコントロールしたい」と願うあなたへ、今日はとてもエキサイティングな技術をお伝えします。
Access VBAを書いていて、「大量のデータから、特定の条件に合う1件をパッと探し出したい」と思ったことはありませんか?
「`DoCmd.FindRecord`を使えばいいのかな?」と思ったそこのあなた。実はそれ、裏の仕組みを知ると、少しもったいないことをしているかもしれません。
今回は、DAO(Data Access Objects)の`FindFirst`という強力なメソッドと、検索結果をスマートに判定する`NoMatch`プロパティを組み合わせて、爆速で正確なデータ検索エンジンを作る極意を伝授します。
ここをクリアすれば、あなたのAccess VBAのスキルは間違いなく中級者の領域に突入します。一緒にマスターしていきましょう!
—
なぜ `FindFirst` なのか?(表計算ソフトとの決定的な違い)
私たちが普段使うExcelは、セルを上から下へ「目視」するように探していきますよね。
しかし、Accessの裏側で動いているデータベースエンジン(Jet / ACE)は、「インデックス(索引)」という魔法の目次を持っています。
`FindFirst`メソッドは、このインデックスをフル活用します。数万件、数百万件のレコードがあったとしても、インデックスが張られたフィールドに対して検索をかければ、一瞬でターゲットのデータにたどり着くのです。
—
高速検索エンジンの基本構造:3つのステップ
DAOを使った検索エンジンは、料理のレシピと同じように、いつも決まった美しい型があります。
1. 「どこを(Table/Query)」 から探すのかを定義する(Recordsetの取得)
2. 「どのインデックスを使って」「何を(FindFirst)」 探すのかを指定する
3. 「見つかったか(NoMatch)」 を判定して処理を分岐する
これらをコードで表現すると、次のようになります。
実践!高速検索プロシージャのサンプルコード
例えば、「社員マスタ」テーブルから、入力された「社員番号」に該当するデータを一瞬で探し出すコードを見てみましょう。
Sub FindEmployeeData()
‘ 【変数宣言】
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim searchID As String
‘ 検索する社員番号(画面のテキストボックス等から取得する想定)
searchID = “E1005”
‘ 1. データベースとレコードセットの取得
Set db = CurrentDb
‘ 【超重要】インデックスを使った高速検索のために、テーブルを直接開く
‘ 検索対象のフィールド(例: 社員番号)には、必ずテーブル側でインデックスを設定しておいてください!
Set rs = db.OpenRecordset(“T_社員マスタ”, dbOpenDynaset)
On Error GoTo ErrorHandler ‘ エラー対策のおまじない
‘ 2. 検索インデックスの指定と FindFirst の実行
‘ ※あらかじめテーブル側で「社員番号」フィールドが主キーまたはインデックスに設定されている必要があります
rs.Index =PrimaryKey ‘ 主キーを使う場合の例(または設定したインデックス名)
‘ ※通常のdynasetであれば、Indexプロパティを使わなくてもFindFirstは動きますが、
‘ 厳密にインデックスを意識させる場合はTableTypeを使います。
‘ 今回は最も汎用的なDynasetで、条件式を直接渡すスマートな方法を使います。
rs.FindFirst “社員番号 = ‘” & searchID & “‘”
‘ 3. 【最重要】NoMatchプロパティによる判定
If rs.NoMatch Then
‘ — 見つからなかった場合の処理 —
MsgBox “該当する社員番号が見つかりませんでした。”, vbExclamation, “検索結果”
Else
‘ — 見つかった場合の処理 —
‘ レコードが見つかった瞬間、カーソルはそのデータにぴたりと重なります。
MsgBox “社員名: ” & rs!社員名 & ” さんが見つかりました!”, vbInformation, “検索成功”
‘ 例:見つかったデータの別のフィールドを書き換えることも自由自在
‘ rs.Edit
‘ rs!最終確認日 = Date
‘ rs.Update
End If
CleanUp:
‘ 【メモリ解放の作法】プロシージャを抜ける前に必ずオブジェクトを閉じ、変数もクリアする
If Not rs Is Nothing Then rs.Close
Set rs = Nothing
Set db = Nothing
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical
Resume CleanUp
End Sub
—
ここで絶対につまずかないための「3つの極意」
初学者の頃、私も何度もハマった「罠」があります。先輩エンジニアからのアドバイスとして、心に刻んでおいてください。
1. `NoMatch` は「エラー」ではなく「状態」である
`FindFirst`でデータが見つからなかったとき、VBAはエラー(実行時エラー)を発生させません。
その代わりに、Recordsetオブジェクトの `NoMatch` プロパティに `True` という「不在証明」を代入して教えてくれます。
だからこそ、`If rs.NoMatch Then` という条件分岐で優しく受け止めてあげる必要があるのです。
2. インデックスがなければ、宝の持ち腐れ
`FindFirst` は強力ですが、検索対象のフィールドにAccessのテーブルデザイン側で「インデックス(はい、重複なし、または重複あり)」を設定していないと、ただの「全件総なめ検索(フルスキャン)」になってしまい、速度がガクッと落ちます。
「FindFirstを使うフィールドには必ずインデックスを貼る」―これをセットで覚えておいてください。
3. オブジェクトの「お片付け(クリーンアップ)」を忘れない
`CurrentDb` や `OpenRecordset` で開いたメモリ上のデータ(Recordset)は、使い終わったら必ず `.Close` し、`Set rs = Nothing` でメモリから解放してあげなければなりません。
これをサボると、見えないところでメモリを食い潰し、Accessが重くなったり不安定になる原因(リソースリーク)になります。上記のコードにある `CleanUp` ラベルの作法を、ぜひあなたの定番にしてください。
—
まとめ:あなたのAccessは、もっと速く、スマートになる
今回は、DAOの `FindFirst` と `NoMatch` を組み合わせた高速検索エンジンの構築方法をお伝えしました。
- 「目視」のExcel感覚から脱却し、インデックスを活用する。
- 見つからない絶望(?)は、`NoMatch` プロパティで優しくハンドリングする。
- 使い終わったオブジェクトは、綺麗に片付けてメモリを守る。
この3つさえ押さえておけば、どれだけデータが増えてもサクサク動く、プロフェッショナルなデータベースアプリケーションが作れるようになります。
ここをクリアしたあなたなら、もうAccess VBAの基礎はバッチリです!
ぜひ今日のコードをご自身の開発環境にコピーして、その「一瞬でデータを見つけ出す快感」を体感してみてくださいね。応援しています!
