【入門編】DAO.Recordsetの「AddNew」と「Update」の間に潜む排他制御の罠と回避策 – Access VBA解析バイブル

スポンサーリンク

こんにちは!Access VBAの世界へようこそ。
ここまでコードを書けるようになったあなたなら、きっと「複数の人が同時に使うシステム」の構築に挑戦している頃だと思います。

今回は、実務で避けて通れない「マルチユーザー環境における排他制御の罠」についてお話しします。

「ボタンを押したらデータを追加するだけ」のシンプルな処理に見えて、実は裏側ではOSやデータベースエンジンが複雑なやり取りを行っています。ここを理解していないと、現場で「突然データが消えた!」「エラーで止まった!」というトラブルに直面することになります。

でも、安心してください。仕組みさえ知ってしまえば、怖くありません。
先輩エンジニアとして、実務でそのまま使える「スマートな回避策」まで優しくガイドしますね。ここをクリアすれば、あなたのAccess VBAのスキルは確実にプロの領域に到達しますよ!

1. なぜ「AddNew」と「Update」の間で事件が起きるのか?

まずは、DAOを使ったレコード追加の基本のおさらいです。普段、私たちはこんな風にコードを書きますよね。

Dim db As DAO.Database
Dim rs As DAO.Recordset

Set db = CurrentDb
Set rs = db.OpenRecordset(“T_顧客マスタ”, dbOpenDynaset)

rs.AddNew
rs!顧客名 = “株式会社サンプル”
rs!登録日 = Date
rs.Update ‘ ←ここで何が起きている?

rs.Close
Set rs = Nothing
Set db = Nothing

このコード、1人で使っている分には何の問題もありません。
しかし、「あなた」と「他のユーザー」が、全く同じ瞬間にこのテーブルへデータを追加しようとしたらどうなるでしょうか?

「書き込み競合(Write Conflict)」の正体

`AddNew`メソッドを実行した瞬間、Access(正確には背後で動いているデータベースエンジン「Jet/ACE」)は、メモリ上に「新しい行のスペース」を確保します。
そして、`Update`メソッドが実行された瞬間に、そのデータを物理的なファイル(.accdb)に書き込みに行きます。

もし、この「書くぞ!」というタイミングで、他のユーザーが同じテーブルの同じような場所をいじっていたり、あるいはファイル全体にロックがかかっていたりすると、Accessはパニックを起こします。

これが、実務者を最も悩ませる「書き込み競合エラー(実行時エラー 3186 または 3260 など)」の正体です。

2. 初学者が陥りがちな「エラーでそのまま止まる」悪夢

マルチユーザー環境で最もやってはいけないのが、エラー対策を何もせずにコードを放置することです。

「あ、エラーが出ちゃった。プログラム停止!」
これでは、ユーザーは「データが保存されなかった!」とパニックになり、何度も同じボタンを押して、しまいには重複データの山が出来上がります。

では、プロはこの「排他制御の罠」をどうやって回避しているのでしょうか?
答えはシンプルです。「もし競合が起きたら、少し待ってからもう一度トライする(リトライ処理)」を実装するのです。

3. 【実践】排他制御を華麗にかわす「リトライ付きAddNew」設計

それでは、実際の業務アプリでそのまま使える、極上の安全設計コードを授けましょう。

エラー番号(`Err.Number`)をトラップし、競合エラー(代表的な3186や3260など)であれば、数回に分けて再挑戦するロジックを組み込みます。

Public Sub SafeAddCustomer(ByVal customerName As String)
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim retryCount As Integer
Dim maxRetry As Integer
Dim success As Boolean

Set db = CurrentDb

maxRetry = 3 ‘ 最大リトライ回数
retryCount = 0
success = False

On Error GoTo ErrorHandler

DojoLoop:
‘ レコードセットを開く
Set rs = db.OpenRecordset(“T_顧客マスタ”, dbOpenDynaset)

rs.AddNew
rs!顧客名 = customerName
rs!登録日 = Date
rs.Update ‘ 排他制御の罠が潜むポイント

success = True
GoTo CleanUp

ErrorHandler:
‘ エラー番号 3186(ファイルロック), 3260(他のユーザーが更新中)などを想定
If Err.Number = 3186 Or Err.Number = 3260 Or Err.Number = 3043 Then
retryCount = retryCount + 1
If retryCount <= maxRetry Then ' オブジェクトを一度解放して再トライの準備 If Not rs Is Nothing Then rs.Close: Set rs = Nothing ' 0.5秒ほど待つ(DoEventsで画面のフリーズを防ぎつつ待機) Dim startTimer As Single startTimer = Timer Do While Timer < startTimer + 0.5 DoEvents Loop Resume DojoLoop ' もう一度トライ! End If End If ' 想定外のエラー、またはリトライ上限オーバー MsgBox "データの保存に失敗しました。" & vbCrLf & _ "エラー内容: " & Err.Description, vbCritical, "排他制御エラー" CleanUp: ' 確実にリソースを解放 On Error Resume Next If Not rs Is Nothing Then rs.Close: Set rs = Nothing Set db = Nothing If success Then ' 成功時の処理 ' MsgBox "データを正常に登録しました。", vbInformation End If End Sub

コードの解説とエンジニアのこだわり

1. `On Error GoTo ErrorHandler` によるトラップ
万が一、他のユーザーとタイミングが被って書き込みに失敗しても、即座にプログラムがクラッシュするのを防ぎます。
2. リトライカウンター (`retryCount`)
無限ループに陥らないよう、最大3回までと回数を制限しています。
3. `Timer` と `DoEvents` によるマイルドな待機
競合が起きた直後に即座に再トライしても、まだ相手がロックを解除していない可能性が高いです。少しだけ(0.5秒)ウェイトを入れることで、ロックが外れるのを優しく待ちます。さらに `DoEvents` を挟むことで、Accessが「フリーズした!」と勘違いして固まるのを防いでいます。

まとめ:ここをクリアすれば、Access VBAの基本はバッチリです!

いかがでしたでしょうか?
今回は、`AddNew` と `Update` の間に潜む排他制御の罠と、そのスマートな回避策について解説しました。

  • なぜ起きる? → 複数人が同時に書き込みを行おうとして、データベースのファイルや行がロックされるため。
  • どう防ぐ? → エラーをキャッチし、少し待ってから「リトライ(再挑戦)」する仕組みをコードに組み込む。

この排他制御の概念は、Access VBAだけでなく、将来的にSQL ServerやVB.NET、C#など、どんなモダンなシステム開発に進んでも必ずあなたを助けてくれる強力な武器になります。

「動くだけのコード」から「現場で壊れない堅牢なコード」へ。
一歩先を行くエンジニアへの階段を、また一段登りましたね。あなたのAccess開発ライフを、これからも応援しています!

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