「なぜか検索が遅い…」を卒業する。DAO `FindFirst` とインデックスの真実
こんにちは。Access VBAの世界へようこそ。
マクロの記録から一歩踏み出し、自分でコードを書き始めたあなたは、もう立派なプログラマーへの階段を登り始めています。
しかし、開発を進めていくうちに、こんな経験をしたことはありませんか?
「最初はサクサク動いていたのに、データが増えた途端に検索がめちゃくちゃ重くなった……」
「コードは間違っていないはずなのに、なぜか動作がもっさりしている……」
実はこれ、コードの書き方が「間違っている」のではなく、「データベースの仕組み(インデックス)を無視してしまっている」ことが原因かもしれません。
今日は、多くの初学者が陥る、DAOの `FindFirst` メソッドにおける `Like` 演算子の罠、そして「インデックスを殺さない書き方」について、その本質を解き明かしていきましょう。ここをクリアすれば、あなたの作るアプリケーションの品質は一段上のレベルへと跳ね上がりますよ。
1. そもそも `FindFirst` とは何者か?
まずは基本の確認です。DAO(Data Access Objects)を使ってレコードセットを開いたとき、特定の条件に一致する最初のレコードを探し出すのが `FindFirst` メソッドです。
' 基本的な使い方のイメージ
rs.FindFirst "顧客名 = '田中太郎'"
これは非常に強力で便利な道具です。しかし、この「検索条件」の中に `Like` 演算子(ワイルドカード検索)を混ぜたとき、パフォーマンスの地獄が始まることがあります。
2. 「インデックス」という名の魔法の索引
ここで、データベースの高速化の要である「インデックス(索引)」について理解しておきましょう。
想像してみてください。あなたは今、1,000ページある分厚い辞書の中から「Tanaka」という単語を探しています。
- インデックスがある状態: 辞書の索引(あいうえお順のページ)を見て、「T」のセクションへ一気に飛び、そこから探す。(爆速!)
- インデックスがない状態: 1ページ目から順番に、全ての単語を読み飛ばしていく。(超絶遅い……)
データベースもこれと同じです。テーブルのフィールドに「インデックス」が設定されていれば、Accessは全データを読み込むことなく、目的のデータへ瞬時にアクセスできます。
3. 【罠】Like演算子の「アスタリスクの位置」が全てを決める
さて、本題です。`Like` 演算子を使ってワイルドカード(`*`)を使うとき、その位置がインデックスを「効かせる」か「殺す」かを決定します。
ここが、エンジニアとしての腕の見せ所です。
❌ パフォーマンスを破壊する書き方(中間一致・後方一致)
' 「〜田中」や「田中〜」など、どこかに田中が含まれるものを探す
rs.FindFirst "顧客名 Like '*田中*'"
この書き方は、データベースから見ると「どこに田中がいるか分からないから、結局1ページ目から全部見なきゃダメだよね」という判断をさせてしまいます。これを専門用語で「フルテーブルスキャン」と呼びます。データが数万件になると、この一瞬の遅延が致命的なストレスになります。
✅ パフォーマンスを最大化する書き方(前方一致)
' 「田中」から始まるものを探す
rs.FindFirst "顧客名 Like '田中*'"
「田中から始まる」という条件であれば、データベースは索引を使って「T」のエリアへジャンプし、そこから「Tanaka…」と続くデータだけを効率よく探し出せます。インデックスが最大限に活用されるのです。
4. 実践:正しいコードの書き方とエラー対策
それでは、実際に現場で使えるコードの形を見てみましょう。
ユーザーが入力した文字を使って検索する、実践的なサンプルです。
Sub OptimizedSearch()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim strSearch As String
Dim strCriteria As String
Set db = CurrentDb
' 顧客テーブルを開く(顧客名フィールドにはインデックスが設定されている前提)
Set rs = db.OpenRecordset("tblCustomers", dbOpenDynaset)
' ユーザーからの入力(例:田中)
strSearch = InputBox("検索したい名前の先頭文字を入力してください")
If strSearch = "" Then Exit Sub
' 【重要】前方一致の条件式を作成する
' 入力値の前後ではなく、「後ろ」だけにアスタリスクをつけるのがコツ!
strCriteria = "顧客名 Like '" & strSearch & "*'"
' 検索実行
rs.FindFirst strCriteria
' 結果の判定
If rs.NoMatch Then
MsgBox "該当するデータは見つかりませんでした。", vbInformation
Else
' 見つかった場合、そのレコードを表示(例として名前を表示)
MsgBox "見つかりました!: " & rs!顧客名
End If
' 後片付け(これ、忘れるとメモリリークの原因になりますよ!)
rs.Close
Set rs = Nothing
Set db = Nothing
End Sub
ここがプロのポイント!
1. `rs.NoMatch` の活用: `FindFirst` は見つからなかったときにエラーを出すのではなく、`NoMatch` プロパティを `True` にします。これを使って、見つからなかった時の処理をスマートに書きましょう。
2. 後片付けの徹底: `Set rs = Nothing`。オブジェクトを使い終わったら解放する。これはAccess VBAにおける「作法」であり、安定したシステムを作るための鉄則です。
5. まとめ:次の一歩へ
最後に、今回の教訓を整理しましょう。
| 検索パターン | 書き方 | インデックスの効き方 | 速度 |
|---|---|---|---|
| 完全一致 | `Name = ‘田中’` | 完璧に効く | 最速 |
| 前方一致 | `Name Like ‘田中*’` | しっかり効く | 高速 |
| 中間一致 | `Name Like ‘*田中*’` | 効かない(死ぬ) | 遅い |
| 後方一致 | `Name Like ‘*田中’` | 効かない(死ぬ) | 遅い |
「どうしても中間一致で検索したいんだ!」という場面もあるでしょう。その場合は、検索対象のテーブル設計を見直すか、あらかじめ検索用の補助フィールドを作るなどの「設計による解決」が必要になります。
「動けばいい」という段階から、「効率よく動く」という段階へ。
この視点を持つだけで、あなたの書くコードはプロの道具へと進化します。
一歩ずつ、確実に。大丈夫、あなたなら必ず使いこなせるようになりますよ。
また次回のレッスンでお会いしましょう!
