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間の「パケットの断片化と通信効率の極限チューニング」について深掘りしようと思う。現場からは以上だ。
