【実務・中級編】DAOのDeleteメソッドで発生する「削除不可」エラーを特定し解決する – Access VBA解析バイブル

スポンサーリンク

DAOの`Delete`メソッドで「削除不可」エラーが起きる本当の原因と、現場で通用する堅牢な例外制御

Access VBAによる業務システム開発において、DAO(Data Access Objects)を用いたデータ操作は基本中の基本だ。しかし、`Recordset.Delete`メソッドを実装した瞬間、理由の分からないランタイムエラーに直面した開発者は少なくないはずだ。

「なぜか一部のレコードだけ削除できない」
「エラー番号は出るが、原因の特定に時間がかかる」

現場でこれを単なる「バグ」として片付けて場当たり的な修正をしているようでは、プロのエンジニアとは言えない。エラーの背後にある「Jet/ACEエンジンのロックメカニズム」と「リ関するリレーショナル整合性」の本質を理解し、一歩先を行く堅牢なエラーハンドリングを実装しよう。

今回は、DAOの削除処理でつまずくすべてのエンジニアに向けて、原因の特定手法から、実務でそのまま使えるプロダクションコードまでを体系的に伝授する。

—

1. なぜ「削除不可」エラーが発生するのか?(根本原因の解剖)

DAOで`Delete`を実行した際、エラー(通常は実行時エラー 3043, 3200, 3197 など)が発生する主な要因は、以下の3つに集約される。

① 参照整合性(Cascading Integrity)の違反

最も多いのが、親テーブルのレコードを削除しようとした際、子テーブルに紐付くレコードが存在し、かつリレーションシップで「カスケード削除」が有効になっていない、あるいは「削除制限」がかかっているケースだ。
データベースの構造上の制約であるため、コード側でガードをかけずに`.Delete`を叩けば容赦なくエラーが飛ぶ。

② ページのマルチユーザロックと競合

Access(Jet/ACEエンジン)はファイルベースのDBゆえに、ページ単位でのロック制御を行っている。
他のユーザ(あるいは自分自身の別のレコードセット)が該当レコードを開いて編集中である場合、あるいはトランザクションのスコープが適切に管理されていない場合、悲観的ロックによって削除がブロックされる。

③ トランザクションの未制御と暗黙のコミット

DAOの`Workspace.BeginTrans`を使わず、あるいは不完全に使った状態でエラーが発生した場合、データベースの状態が中途半端になり、次回の操作に悪影響を及ぼす。

—

2. デバッグの極意:エラーを握りつぶさず「原因」を特定する

初学者によくある悪手は、`On Error Resume Next`でエラーを完全に握りつぶすことだ。これではデータ破損や不整合の温床になる。

実務における正しいデバッグ・アプローチは、「エラー番号をトラップし、何が原因で拒絶されたのかをコード上で判定する」ことである。

DAOの主な削除関連エラーコード:

  • Err 3200: 「レコードは、別のユーザまたは別のプロセスによってロックされています。」
  • Err 3065: 「不正なSQL文です。」(※SQLベースの場合)
  • Err 3197: 「別のユーザーが同じデータを同時に変更したため、データが変更されました。」

これらのトラップを見据え、「安全に削除を試行し、失敗した場合は理由を特定してユーザーに分かりやすく通知する」アーキテクチャを構築する。

—

3. 【プロダクションコード】堅牢なDAO削除モジュール

ここに示すのは、実務の基幹システムでも耐えうる、トランザクション制御と詳細なエラーハンドリングを実装したプロフェッショナル向けのマクロである。

Option Explicit

‘==============================================================================
‘ 処理名 : SafeDeleteRecord
‘ 概要 : 指定されたIDのレコードを安全に削除する(DAOトランザクション制御付き)
‘ 引数 : targetID – 削除対象の主キー値
‘ 戻り値 : Boolean – 成功時はTrue、失敗時はFalse
‘==============================================================================
Public Function SafeDeleteRecord(ByVal targetID As Long) As Boolean
Dim ws As DAO.Workspace
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim strSQL As String

‘ 戻り値の初期化
SafeDeleteRecord = False

‘ ワークスペースとデータベースの参照を取得(デフォルトワークスペース)
Set ws = DBEngine.Workspaces(0)
Set db = CurrentDb

‘ トランザクションの開始
ws.BeginTrans

On Error GoTo ErrorHandler

‘ 対象レコードを排他制御を意識して開く(Pessimistic Lockingの準備)
strSQL = “SELECT FROM T_Orders WHERE OrderID = ” & targetID
Set rs = db.OpenRecordset(strSQL, dbOpenDynaset, dbDenyWrite)

‘ レコードの存在確認
If rs.EOF And rs.BOF Then
MsgBox “指定されたレコード(ID: ” & targetID & “)は既に存在しません。”, vbExclamation, “削除エラー”
rs.Close
ws.Rollback
Exit Function
End If

‘ 削除実行
rs.Edit ‘ 削除前には必ず編集状態または削除メソッドを実行可能にする
rs.Delete

‘ トランザクションのコミット(すべての安全確認を通過)
ws.CommitTrans

SafeDeleteRecord = True
MsgBox “レコードの削除が正常に完了しました。”, vbInformation, “処理成功”

CleanUp:
‘ オブジェクトの厳格な解放(メモリリーク防止)
On Error Resume Next
If Not rs Is Nothing Then rs.Close: Set rs = Nothing
Set db = Nothing
Set ws = Nothing
Exit Function

ErrorHandler:
‘ トランザクションのロールバック(変更の破棄)
ws.Rollback

‘ エラー番号に応じた実務的なメッセージング
Select Case Err.Number
Case 3200
MsgBox “このデータは現在、他のユーザーまたは画面で編集中のため削除できません。” & vbCrLf & _
“他の作業を終了してから再度お試しください。”, vbCritical, “ロック競合エラー”
Case 3043
MsgBox “ネットワーク接続またはファイルへのアクセスに失敗しました。”, vbCritical, “I/Oエラー”
Case Else
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“Error No: ” & Err.Number & vbCrLf & _
“Description: ” & Err.Description, vbCritical, “システムエラー”
End Select

Resume CleanUp
End Function

—

4. チーフアーキテクトからの設計上のアドバイス

上記のコードを見れば、単に「`rs.Delete`と書くだけのコード」がいかに脆弱であるかが分かるはずだ。現場で開発する際は、以下の3点を必ず遵守してほしい。

1. トランザクションは必ず明示的に張る
複数テーブルにまたがる削除、あるいは参照整合性の絡む削除では、途中でエラーが起きた場合にデータベースが腐敗(孤児レコードの発生)する。`BeginTrans` と `Rollback` のセットはプロの最低限の義務である。
2. オブジェクトの確実なスコープ破棄
`Recordset` や `Database` を開きっぱなしにすると、Jet/ACEエンジンのリソースを圧迫し、デッドロックや「リソース不足」エラーを引き起こす。`CleanUp` ラベルを作り、確実に `Nothing` 代入を行ってメモリを解放せよ。
3. 生のエラーメッセージをユーザーに見せない
「キーが重複しています」「ロックされています」といった英語混じりのシステムメッセージをそのままエンドユーザーに見せるのは、システムの品位を下げるだけでなく、無駄な問い合わせを増やす原因になる。必ず `Select Case Err.Number` でラップし、業務文脈に落とし込んだ日本語メッセージに変換すること。

DAOの仕様を熟知し、裏側のデータベースエンジンがどう動いているかを想像しながらコードを書く。このアプローチさえ身につければ、AccessVBAにおけるデータ操作で恐れるものは何もない。自身のプロジェクトのコードを今すぐ見直し、堅牢なアーキテクチャへとブラッシュアップしてほしい。

タイトルとURLをコピーしました