こんにちは!Access VBAの世界へようこそ。
マクロの記録から一歩踏み出し、「自分だけのシステムを自分の手でコントロールしたい」というあなたを、今日は一段上のステージへお連れします。
ここをクリアすれば、Access VBAの基本はバッチリですよ。自信を持って進んでいきましょう!
—
マルチユーザー環境の「魔物」:データ競合を防ぐ排他制御
Accessで作ったデータベースを、社内の複数メンバーで同時に使うことってありますよね。
ここでよく起こるのが、こんな悲劇です。
> Aさん:「あれ?さっき顧客データの電話番号を変えたのに、Bさんが保存した上書きデータで元に戻っちゃったよ!」
> Bさん:「え、そんなの知らないよ。こっちはさっき画面を開いてそのまま保存したもん!」
――これがデータの競合です。
複数人が同時に同じレコード(行)を書き換えようとしたとき、どちらかの変更が闇に葬られてしまう恐怖の現象。これを防ぐための仕組みが「排他制御(はいたせいぎょ)」です。
そして、この排他制御の鍵を握るのが、DAO.Recordsetオブジェクトの `LockEdits`(ロックエディッツ) プロパティなのです。
—
LockEditsプロパティとは?(悲観と楽観の選択)
DAOを使ってレコードを更新するとき、VBAに「いつ、どのタイミングで鍵をかけるか」を指示するのが `LockEdits` です。
選択肢は大きく分けて2つあります。
1. 悲観的排他制御(Pessimistic Locking)
- 設定値: `dbOptimistic` ではなく `dbPessimistic`(※名前に反してこちらが悲観的!)
- 考え方: 「世の中は信用できない!俺が編集し始めた瞬間から、他のやつにはこのレコードを触らせねぇ!」
- 特徴: レコードを `Edit` メソッドで開いた瞬間から、保存(`Update`)するまで他のユーザーはそのレコードを編集できません。安全確実ですが、他の人が待たされるため、多人数環境ではシステムが重くなる原因に。
2. 楽観的排他制御(Optimistic Locking)
- 設定値: `dbOptimistic`
- 考え方: 「まあ、みんな善人だから同時に同じ場所を書き換えるなんて滅多にないさ。保存する直前にだけ、他の人がいじってないか確認しようぜ」
- 特徴: 編集中のロックはかけません。保存の瞬間だけチェックし、もし他の人に先を越されていたらエラーを出して教えます。現代のWebや業務アプリの主流です。
—
【実践】コードで見る違いと書き方
それでは、実際のVBAコードを見てみましょう。
今回は、顧客テーブル(`T_顧客`)の電話番号を書き換える処理を例にします。
パターンA:安全第一!「悲観的排他制御」のコード
Sub UpdateCustomer_Pessimistic()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Set db = CurrentDb
‘ 【悲観的】レコードを開いた瞬間からロックをかける
Set rs = db.OpenRecordset(“SELECT FROM T_顧客 WHERE 顧客ID = 1”, dbOpenDynaset, dbDenyWrite)
‘ ※厳密にはLockEditsプロパティで制御します
Set rs = db.OpenRecordset(“SELECT FROM T_顧客 WHERE 顧客ID = 1”, dbOpenDynaset)
rs.LockEdits = dbPessimistic ‘ 悲観的ロックの指定
If Not rs.EOF Then
rs.Edit ‘ 編集開始(この瞬間、他のユーザーはこのレコードを触れなくなります)
rs!電話番号 = “03-0000-0000”
‘ エラーハンドリングを省略していますが、ここで保存します
rs.Update
MsgBox “更新完了しました!”, vbInformation
End If
rs.Close
Set rs = Nothing
Set db = Nothing
End Sub
パターンB:実務の主流!「楽観的排他制御」+ エラーハンドリング
実務で圧倒的に推奨されるのは、この楽観的排他制御です。
「同時編集の衝突」が起きたときに、VBAがパニックを起こさず、優しくユーザーに教えてあげるためのエラートラップ(Err.Number = 3186 または 3197)を仕込みます。
Sub UpdateCustomer_Optimistic()
Dim db As DAO.Database
Dim rs As DAO.Recordset
On Error GoTo ErrorHandler
Set db = CurrentDb
‘ 楽観的ロックでレコードを開く(デフォルトもこれです)
Set rs = db.OpenRecordset(“SELECT FROM T_顧客 WHERE 顧客ID = 1”, dbOpenDynaset)
rs.LockEdits = dbOptimistic
If Not rs.EOF Then
rs.Edit
rs!電話番号 = “03-9999-9999”
‘ 【重要】ここで他のユーザーに書き換えられていないかがチェックされます
rs.Update
MsgBox “データをスマートに更新しました!”, vbInformation
End If
CleanExit:
On Error Resume Next
rs.Close
Set rs = Nothing
Set db = Nothing
Exit Sub
ErrorHandler:
‘ 3197: 「別のユーザーがこのデータを変更しました」エラー
If Err.Number = 3197 Then
MsgBox “他のユーザーが既にこの顧客データを変更しています。” & vbCrLf & _
“最新の情報を再読み込みしてやり直してください。”, vbExclamation, “競合発生”
Else
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical
End If
Resume CleanExit
End Sub
—
陥りやすい罠とエンジニアの知見
ここで、現場でよくある「やっちまった!」ポイントをシェアしておきますね。
1. `Edit` の前に `LockEdits` を書くこと!
`LockEdits` プロパティは、必ず `OpenRecordset` の後、かつ `Edit` メソッドを呼び出す前に設定してください。順序を間違えると、設定が無視されてしまいます。
2. 長時間の `Edit` 放置は厳禁
特に悲観的ロック (`dbPessimistic`) を使っているときに、VBAの処理の中で `MsgBox` を挟んだり、ユーザーの入力待ちをさせたりすると、その間ずっとテーブルのレコードがロックされ続け、他の人が一切作業できなくなります(デッドロックや業務ストップの原因)。ロック期間は極限まで短くするのがプロの流儀です。
—
まとめ
- 悲観的 (`dbPessimistic`):絶対に競合させたくない重要データに。ただしロック時間が長くなりがち。
- 楽観的 (`dbOptimistic`):通常の業務アプリならこちら。競合時はエラー番号 `3197` を捕捉して優しく案内する。
排他制御をマスターすると、Accessアプリが一気に「プロ仕様の堅牢なシステム」に生まれ変わります。
最初は難しく感じるかもしれませんが、コードのテンプレートを手元に置いておけば大丈夫。ぜひ、あなたの開発環境でも試してみてくださいね!
