ACCESS カレントレコードがありません、の対処方法
ACCESSで「カレントレコードがありません」というメッセージがでたら、それはプログラムに問題がある可能性があります。
今回は、カレントレコードがありません、の対処方法をご紹介します。
こんにちは。
はこにわガジェット (@hakoniwagadget) です。
ACCESSを使った売上管理、顧客管理などのデータベース開発を行っています。
ACCESSは基本機能だけでも十分便利ですが、VBAを使うことで格段に使いやすいデータベースを作成可能です。
この記事ではACCESSでのVBAの使い方をご紹介していきます。
目次
カレントレコードがありません、の原因
ACCESSでのエラーで、実行時エラー3021「カレントレコードがありません」もしくは「BOFとEOFのいずれかがTrueになっているか、または現在のレコードが削除されています。要求された操作には、現在のレコードが必要です」というメッセージが出るのは、記載されている通りレコードがないためです。
これはVBAでレコード処理を行う中で、例えば以下のようなケースで発生します。
・テーブルにフィルターをかけた結果、レコードがないにもかかわらず処理を実施しようとした
・ループ処理ですべてのレコードを処理し終わってレコードがないにも関わらず処理を継続した
以後、実際にどのようなシーンで発生し、どう回避すればよいかを説明します。
サンプルテーブル
今回はテスト用に以下のサンプルテーブルを準備しました。
売上情報が格納されたテーブルです。
テーブルにフィルターをかけた結果、レコードがないにもかかわらず処理を実施しようとした
1つ目の例はテーブルにフィルターをかけた結果、レコードがないにもかかわらず処理を実施しようとした場合です。
以下のプロシージャを準備しました。
Public Sub test_current()
'変数を定義
Dim cnn As ADODB.Connection
Dim rst1 As ADODB.Recordset
Dim kokyaku_id As Long
'変数にADOオブジェクトを代入
Set cnn = CurrentProject.Connection
Set rst1 = New ADODB.Recordset
rst1.CursorLocation = adUseClient
'レコードセットを取得
rst1.Open "test_売上", cnn, adOpenKeyset, adLockOptimistic
rst1.Filter = "顧客ID = 30"
kokyaku_id = rst1!顧客ID
MsgBox "処理終了"
'終了処理
rst1.Close: Set rst1 = Nothing
cnn.Close: Set cnn = Nothing
End Sub
ADOを使って、ACCESSのテーブルを操作するプロシージャです。
rst1.Open "test_売上", cnn, adOpenKeyset, adLockOptimistic
の部分で、rst1というレコードセット変数に、先に紹介したtest_売上テーブルを格納しています。
そして、
rst1.Filter = "顧客ID = 30"
でレコードセットを顧客IDが30のものだけにフィルターしています。
ここで、test_売上の内容を確認して下さい。
顧客IDが30のレコードは存在しないですよね。
そのため、このフィルターを掛けた段階でレコード数が0になってしまっています。
その後に
kokyaku_id = rst1!顧客ID
と、rst1を参照した処理を入れているのでここでエラーが出てしまうのです。
このように、フィルター処理をした後は意図せずにレコード数が0になってしまうことがあります。
こうしたエラーを防ぐためには、フィルター後には必ずレコードセット数を調べ、レコードセットが0であれば処理を終了するようにしておきましょう。
今回の例では、フィルター処理の後に
rst1.Filter = "顧客ID = 30"
If rst1.RecordCount = 0 Then
Exit Sub
End If
として、RecordCountでレコード数を調べ、0であればExit Subでプロシージャを終了する処理を追加しておくことでエラーを回避できます。
フィルター処理を行うたびに、この処理は追加しておくとよいでしょう。
ループ処理ですべてのレコードを処理し終わってレコードがないにも関わらず処理を継続した
では、もう一つのパターンである、ループ処理ですべてのレコードを処理し終わってレコードがないにも関わらず処理を継続した、の例を見てみましょう。
以下のプロシージャがサンプルです。
Public Sub test_current()
'変数を定義
Dim cnn As ADODB.Connection
Dim rst1 As ADODB.Recordset
Dim kokyaku_id As Long
'変数にADOオブジェクトを代入
Set cnn = CurrentProject.Connection
Set rst1 = New ADODB.Recordset
rst1.CursorLocation = adUseClient
'レコードセットを取得
rst1.Open "test_売上", cnn, adOpenKeyset, adLockOptimistic
rst1.Sort = "顧客ID"
rst1.MoveFirst
kokyaku_id = rst1!顧客ID
Do Until rst1.EOF
Do Until rst1!顧客ID <> kokyaku_id
kokyaku_id = rst1!顧客ID
rst1.MoveNext
Loop
kokyaku_id = rst1!顧客ID
rst1.MoveNext
Loop
MsgBox "処理終了"
'終了処理
rst1.Close: Set rst1 = Nothing
cnn.Close: Set cnn = Nothing
End Sub
こちらもADOを使ってレコードを処理するプロシージャです。
途中で、レコードセットrst1のループ処理が行われています。
Do Until rst1.EOF
Do Until rst1!顧客ID <> kokyaku_id
kokyaku_id = rst1!顧客ID
rst1.MoveNext
Loop
kokyaku_id = rst1!顧客ID
rst1.MoveNext
Loop
この部分です。
Do…Loopが二重に構成されていますが、外側のループはrst1のEOF、つまり最終レコードまでのループとなっており、内側のループはrst1の顧客IDがkokyaku_idと異なるまで、となっています。
変数:kokyaku_idには一つ前のレコードの顧客IDを格納するようにしていますので、つまり前のレコードと顧客IDが異なっていたら内側のループを脱出するという処理になっているわけです。
rst1のレコードを前に進める処理は、内側のループ条件に合致したときは内側のループのrst1.MoveNextで、合致しなかった場合は外側のループのrst1.MoveNextで行われます。
そのため、rst1.MoveNextが重複することもなく、処理的には問題無いように見えます。
しかし、このプロシージャには欠陥があります。
それは、最終レコードの顧客IDが、その1つ前のレコードの顧客IDと同じだった場合です。
実際にサンプルで準備したtest_売上テーブルを顧客IDでソートすると、顧客IDの最大値である27のレコードが2つあります。
1つ前のレコードの顧客IDはkokyaku_idに格納されますので、最終レコードの顧客IDも同じということは
Do Until rst1!顧客ID <> kokyaku_id
はどちらも27になって条件を満たし、内側のループに入ります。
すると、rst1.MoveNextでEOFにたどり着いているにも関わらず、ループを脱出せずに再度内側のループを回してしまってエラーが出るのです。
この問題は、rst1が最終レコードになっていた場合はループを脱出する処理を追加することで解決できます。
修正したプロシージャが以下です。
Public Sub test_current()
'変数を定義
Dim cnn As ADODB.Connection
Dim rst1 As ADODB.Recordset
Dim kokyaku_id As Long
'変数にADOオブジェクトを代入
Set cnn = CurrentProject.Connection
Set rst1 = New ADODB.Recordset
rst1.CursorLocation = adUseClient
'レコードセットを取得
rst1.Open "test_売上", cnn, adOpenKeyset, adLockOptimistic
rst1.Sort = "顧客ID"
rst1.MoveFirst
kokyaku_id = rst1!顧客ID
Do Until rst1.EOF
Do Until rst1!顧客ID <> kokyaku_id
kokyaku_id = rst1!顧客ID
Debug.Print "内:顧客ID:" & rst1!顧客ID
rst1.MoveNext
'追加
If rst1.EOF = True Then
Exit Do
End If
Loop
'追加
If rst1.EOF = True Then
Exit Do
End If
kokyaku_id = rst1!顧客ID
Debug.Print "外:顧客ID:" & rst1!顧客ID
rst1.MoveNext
Loop
MsgBox "処理終了"
'終了処理
rst1.Close: Set rst1 = Nothing
cnn.Close: Set cnn = Nothing
End Sub
‘追加と書いてある2か所を追記しています。
追記した内容はどちらも同じで以下です。
If rst1.EOF = True Then
Exit Do
End If
これは、rst1がEOF、つまり最終レコードに到達していればループを脱出する、という処理です。
これをそれぞれのDo…Loop内に入れることで問題無く動作させることが可能です。
以上、カレントレコードがありません、の対処方法をご紹介しました。
ACCESSを使いこなせば、業務の効率化や自動化が実現できます。
しかし、自分でACCESSを学ぶには時間がない、難しそうで不安、という方も多いでしょう。
そんな時は、ACCESS開発歴20年以上、過去に100以上のACCESSデータベースを開発してきた私(@hakoniwagadget)にお任せください。
ACCESSの新規開発、既存のACCESSの修正、ACCESSの操作レッスンなど様々なサービスをご提供しています。
ご興味のある方は下記のサービス一覧をご覧ください。
最後までお読みいただき、ありがとうございました。