【テクニカル・上級編】CurrentDb.Executeの「dbSeeChanges」オプションが必要なケースとSQL Server連携の注意点 – Access VBA解析バイブル

スポンサーリンク

AccessからSQL Serverを操る深淵:dbSeeChangesが握る「整合性の鍵」

現場で長年Accessを使い込んでいるエンジニアほど、ある日突然突きつけられる「実行時エラー 3622」に言葉を失う。
「ODBC 接続を使用する場合、dbSeeChanges オプションが必要です」。

このエラーは、Accessが単なるローカルDBからSQL Serverというエンタープライズ級のバックエンドに足を踏み入れた瞬間に現れる、いわば「洗礼」だ。なぜこのオプションが必要なのか、そして我々エンジニアがこの「不穏な要求」とどう対峙すべきか。今回は、その深淵を紐解いていく。

1. なぜ「dbSeeChanges」は強制されるのか

結論から言えば、これは「楽観的同時実行制御」の要請である。

Accessのローカルエンジン(ACE/Jet)は、ファイルベースで排他制御を行う。しかし、SQL Serverは行単位での並行処理が基本だ。SQL Server上のテーブルに「オートナンバー(IDENTITY)」や「レプリケーションID(uniqueidentifier)」が存在する場合、Accessは「他の誰かが同時にこの行を書き換えていないか?」を逐一監視しなければならない。

`dbSeeChanges` を付与するということは、Accessに対して「SQL Serverの行バージョン管理メカニズムを介して、競合が発生した場合は即座に例外を投げてくれ」と指示を出す行為に他ならない。これを怠れば、データの整合性は一瞬で崩壊する。

2. 現場で生きる実装:CurrentDb.Execute の最適解

`DoCmd.RunSQL` は即座に捨てろ。あれはUI経由で動かすためのものであり、トランザクションの制御もままならない。我々が使うべきは `CurrentDb.Execute` だ。

以下のコードは、トランザクションを確実に制御し、メモリのリークを防ぐための「最低限の作法」である。

Public Sub ExecuteSqlToServer(ByVal strSql As String)
Dim db As DAO.Database

‘ CurrentDbを直接呼び出すのは非効率。一度オブジェクトに代入し、再利用する。
‘ ※CurrentDbは呼び出すたびに内部的な再構築が走るため、高負荷なループ内では命取りとなる。
Set db = CurrentDb

On Error GoTo Err_Handler

‘ トランザクションを開始し、不可分性を保証する
DBEngine.BeginTrans

‘ dbSeeChangesを付与して実行。
‘ dbFailOnErrorは、エラー時に自動ロールバックをトリガーする。
db.Execute strSql, dbSeeChanges Or dbFailOnError

DBEngine.CommitTrans

Exit_Proc:
‘ オブジェクトの明示的解放。VBAのガベージコレクションを信じるな。
Set db = Nothing
Exit Sub

Err_Handler:
‘ 予期せぬエラー発生時のロールバック
DBEngine.Rollback
Debug.Print “Error: ” & Err.Number & ” – ” & Err.Description
Resume Exit_Proc
End Sub

3. シニアエンジニアが意識すべき「見えない壁」

メモリと接続のライフサイクル

`CurrentDb` をプロシージャ内で生成し、`Set = Nothing` で解放する。これは基本中の基本だが、さらに上位を目指すなら「DAO.Databaseオブジェクトをモジュールレベルで保持し、アプリケーション終了まで使い回す」手法もある。ただし、これは接続のタイムアウトや切断検知の実装を伴うため、実装コストとのトレードオフになる。

ODBC接続の「罠」:タイムスタンプ型

SQL Server側で `rowversion`(旧 timestamp)型がテーブルに含まれている場合、Accessはさらに神経質になる。この場合、`dbSeeChanges` は必須であるだけでなく、テーブル側に主キーが適切に設定されていることが前提となる。主キーのないテーブルをAccessからリンクし、更新しようとすれば、どんな呪文を唱えてもシステムは崩壊する。

Windows APIとの連携

もし、大規模なバッチ処理を行う際、Accessが「応答なし」になるのを防ぎたいのであれば、`DoEvents` をループ内に適切に配置し、場合によっては `Sleep` 関数(kernel32)を呼び出してCPUリソースを解放する。

‘ 負荷分散のための宣言
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)

‘ ループ内での実装例
For i = 0 To UBound(dataArray)
‘ …処理…
If i Mod 100 = 0 Then
DoEvents
Sleep 10 ‘ CPUを0.01秒間開放し、OSの応答性を維持する
End If
Next i

結論:技術は「おまじない」ではない

`dbSeeChanges` は単なる「エラー回避のためのオプション」ではない。それは、「我々は今、マルチユーザーの分散環境でデータを扱っている」という事実を、Accessという古き良きフレームワークに突きつけるためのフラグである。

レガシーなAccess環境だからこそ、プロトコルやエンジンの挙動を理解し、メモリ管理からトランザクション設計までを制御下に置く。それこそが、伝説的なシステムアーキテクトが現場で証明し続ける「技術の真髄」である。

次回の更新では、AccessとSQL Server間の「パケットの断片化と通信効率の極限チューニング」について深掘りしようと思う。現場からは以上だ。

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