こんにちは!Access VBAの世界へようこそ。
マクロの記録から一歩踏み出し、自分の手でコードを書いて業務を自動化しようとしているあなたへ、今日はとても大切な「知見」をお伝えしますね。
データベースを操作していて、こんな不思議な現象に出会ったことはありませんか?
「あれ? ちゃんとデータはあるはずなのに、RecordCountを見たら『-1』が返ってくる……!?」
初心者の方が必ずと言っていいほどハマるこの罠。実はこれ、Accessのデータベースエンジン(DAO)ではなく、外部データベースなどと接続するADO(ActiveX Data Objects)を使うときに必ず知っておくべき「カーソル」の仕様なんです。
ここをクリアすれば、あなたのVBAスキルは確実にワンランク上のエンジニア領域に到達します。一緒にスッキリ解決していきましょう!
—
1. なぜRecordCountは「-1」になってしまうのか?
まず結論からお伝えすると、ADOのRecordCountが `-1` を返すのは、「今、開いているテーブルの全貌が、プログラム側から見えていない状態だから」です。
イメージしてみてください。
あなたは巨大な倉庫(データベース)の管理人です。新人のアルバイト君に「倉庫にダンボールがいくつあるか数えてきて!」と指示を出しました。
- パターンA(前方浄軟カーソル / デフォルト)
アルバイト君は、入り口から一方通行で奥へ進みながら数えます。手前に戻ることはできません。この方法、実は「全体でいくつあるか」を把握しながら歩くことができないのです。今いくつ目か、というカウントアップはできても、全体の総数は最後まで歩ききらないと分かりません。そのため、ADOは「今、総数は分からないよ」という意味で `-1` を返します。
- パターンB(静的カーソル)
アルバイト君は、倉庫の写真をパシャリとスマホで撮りました。その写真を見れば、ダンボールの総数が一目で分かりますよね。
ADOでレコードセットを開くとき、何も指定しないとデフォルトで「パターンA(正確には ForwardOnly や Keyset など)」で接続されます。これが、件数が `-1` になる正体です。
—
2. 解決策:正しいレコード数の取得法
「じゃあ、常に正確なレコード数が欲しいときはどうすればいいの?」
答えは大きく分けて2つあります。状況に合わせて使い分けるのがプロの技です。
解決策①:カーソルタイプを「静的(adOpenStatic)」に変更する
一番直感的な方法です。レコードセットを開くときの「カーソルタイプ」を、先ほどの“写真に撮る方式(静的カーソル)”に変更します。
以下のコードを見てください。ここが今日のハイライトです!
Sub GetRecordCount_Static()
Dim conn As Object
Dim rs As Object
‘ 1. コネクションオブジェクトの作成
Set conn = CreateObject(“ADODB.Connection”)
‘ ※ここでは分かりやすくするため、カレントDBに接続する例とします
conn.Open “Provider=Microsoft.ACE.OLEDB.12.0;Data Source=” & CurrentProject.FullName
‘ 2. レコードセットオブジェクトの作成
Set rs = CreateObject(“ADODB.Recordset”)
‘ ★ここが重要!カーソルタイプを「adOpenStatic (3)」に指定する
‘ (定数を使う場合は adOpenStatic ですが、数値の 3 を指定しても動きます)
‘ 第3引数:3 = adOpenStatic, 第4引数:1 = adLockReadOnly
rs.Open “SELECT FROM T_社員マスター”, conn, 3, 1
‘ 3. レコード数の確認
If rs.RecordCount <> -1 Then
MsgBox “レコード数は ” & rs.RecordCount & ” 件です!”, vbInformation
Else
MsgBox “レコード数が取得できませんでした。”, vbExclamation
End If
‘ 4. 後片付け(オブジェクトの解放はエンジニアのたしなみです)
rs.Close
conn.Close
Set rs = Nothing
Set conn = Nothing
End Sub
このコードのポイントは、`rs.Open` の引数に `3` (adOpenStatic) を指定している点です。これだけで、`rs.RecordCount` は正しく全件数を返してくれるようになります。
—
解決策②:SQLの「COUNT関数」を使う(実務で最もおすすめ!)
実は、パフォーマンス(処理の速さ)を極める現場のエンジニアほど、`RecordCount` プロパティをあまり使いません。なぜなら、数千、数万件のデータをメモリ上にすべて読み込もうとすると、それだけでPCに負荷がかかるからです。
「件数知りたいだけなら、最初からデータベースに数えてもらえばいいじゃん!」
これが、SQLの `COUNT` 関数を使うアプローチです。
Sub GetRecordCount_BySQL()
Dim conn As Object
Dim rs As Object
Dim lngCount As Long
Set conn = CreateObject(“ADODB.Connection”)
conn.Open “Provider=Microsoft.ACE.OLEDB.12.0;Data Source=” & CurrentProject.FullName
‘ ★「データを取ってくる」のではなく、「件数を数えた結果を1行だけ取ってくる」
Set rs = conn.Execute(“SELECT COUNT() AS データの件数 FROM T_社員マスター”)
‘ 取得した1行目の値を変数に格納
lngCount = rs.Fields(“データの件数”).Value
MsgBox “SQLで取得したレコード数は ” & lngCount & ” 件です!”, vbInformation
‘ 後片付け
rs.Close
conn.Close
Set rs = Nothing
Set conn = Nothing
End Sub
【この方法が優れている理由】
データ本体をゴソッと取得するのではなく、「件数という数字(1つのセル)」だけをピンポイントで取得するため、メモリを消費せず、圧倒的に動作が軽くなります。実務ではこちらを使う機会のほうが多いかもしれませんね。
—
3. まとめ:今日から使えるエンジニアの知見
ここまでの内容をギュッとまとめます。
1. ADOのRecordCountが `-1` になるのは、デフォルトのカーソルが原因。
2. どうしてもRecordCountプロパティを使いたいときは、カーソルタイプを `adOpenStatic (3)` に設定する。
3. 件数確認が目的なら、`SELECT COUNT()` を使ったSQLアプローチの方がパフォーマンス面で圧倒的に有利。
「動くけれど、なぜそうなるのか分からない」状態から、「仕組みを知った上でコードをコントロールできる」状態へ。このステップを越えると、Access VBAを書くことが一気に楽しく、そして自信に満ちたものに変わっていきます。
ここをクリアしたあなたなら、もう基本はバッチリです!
明日の開発現場で、ぜひこのテクニックを使ってみてくださいね。あなたのVBAライフを、チーフアーキテクトとしてこれからも応援しています!
