ACCESS カレントレコードがありません、の対処方法

カレントレコードがありません、の対処方法

ACCESSで「カレントレコードがありません」というメッセージがでたら、それはプログラムに問題がある可能性があります。

今回は、カレントレコードがありません、の対処方法をご紹介します。


こんにちは。
はこにわガジェット (@hakoniwagadget) です。

ACCESSを使った売上管理、顧客管理などのデータベース開発を行っています。
ACCESSは基本機能だけでも十分便利ですが、VBAを使うことで格段に使いやすいデータベースを作成可能です。
この記事ではACCESSでのVBAの使い方をご紹介していきます。

カレントレコードがありません、の原因

ACCESSでのエラーで、実行時エラー3021「カレントレコードがありません」もしくは「BOFとEOFのいずれかがTrueになっているか、または現在のレコードが削除されています。要求された操作には、現在のレコードが必要です」というメッセージが出るのは、記載されている通りレコードがないためです。

これはVBAでレコード処理を行う中で、例えば以下のようなケースで発生します。

・テーブルにフィルターをかけた結果、レコードがないにもかかわらず処理を実施しようとした

・ループ処理ですべてのレコードを処理し終わってレコードがないにも関わらず処理を継続した

以後、実際にどのようなシーンで発生し、どう回避すればよいかを説明します。

サンプルテーブル

今回はテスト用に以下のサンプルテーブルを準備しました。
売上情報が格納されたテーブルです。

test_売上

テーブルにフィルターをかけた結果、レコードがないにもかかわらず処理を実施しようとした

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の操作レッスンなど様々なサービスをご提供しています。
ご興味のある方は下記のサービス一覧をご覧ください。

サービス一覧

最後までお読みいただき、ありがとうございました。