先日、ADO経由でVB6によるテーブルデータ編集のプログラムを書いていたところ、表題のエラー
実行時エラー ‘-2147217864 (80040e38)’]エラー
行が見つからなかっため、更新できません。列の値は最後に読み込まれた後で変更された可能性があります。
が表示されて、この対策にドはまりしたのでメモしておく。

2147217864 (80040e38)エラー

Microsoft Cursor サービスあたりからスローされる

次のような条件で発生することがある

3つのフィールドが存在するテーブルがあったとする。

Dim dbs As New ADODB.Connection
Dim rs  As New ADODB.Recordset

dbs.Open “Provider=Microsoft.ACE.OLEDB.12.0;Persist Security Info=False;Data Source=C:\company.accdb”
rs.CursorLocation = adUseClient
rs.Open “SELECT * FROM meibo”, dbs, adOpenStatic, adLockOptimistic

‘    1行追加
rs.AddNew
rs!fullName = “Suzuki”
rs.Update

‘    追加した行を探す
rs.MoveFirst
rs.Find “fullName = ‘Suzuki'”

‘    その行を修正する
rs!fullName = “Ichiro Suzuki”
rs!Address = “111 Central Park N, New York, NY”
rs!Age = 40
rs.Update    ‘ ←この行でエラーが発生することがある

rs.Close
dbs.Close

Set rs = Nothing
Set dbs = Nothing

原因は?

同条件でも発生するとき・しないときが不安定で、いまひとつ再現性に乏しいが
値を指定していない列(フィールド)
がAddNewで生じているときにこのエラーが起きることがある。

なんとなくだが、AddNewの際に、ADO(カーソルサービス)が 未指定フィールドを、Null など 別の文字に変えるなどして自動補正をかけてしまい、そのことが読み込まれた後で変更された可能性という解釈になってんじゃないかと思っている。

対応方法

対策は2つ考えられ、

  1. AddNewのとき 未指定の空文字列を残さない(残したまま移動しない)
  2. ADOのキャッシュにAddNewした「後」のデータを、正式に読み込ませる

という方法がある。

1 は、AddNewのとき漏れなく全部指定してからUpdateする方法である。

2 なら、例示のソースであれば
‘    1行追加
rs.AddNew
rs!fullName = “Suzuki”
rs.Update
rs.Requery

として、実際に記録されているデータをADOキャッシュに取り込み、矛盾を解消する方法である。

いずれも有効だが、1 の方法が オーバーヘッドが最も少ないと思う。