いつもお世話になっております。
表題のとおり、updateメソッドについてなのですが、アクセスのa.mdbをデータソースとして、テーブルBに10000万レコード新規でインサートします。その際にテーブルBのデータセット・データテーブルに1000レコード貯めたら一気に挿入というロジックを考えて実践しましたが1000の倍数以外{ようは最後の残りレコード)例:1003 レコードなら残り3レコードが登録されないという減少に悩んでいます。updateを複数回行うともんだいがるのでしょうか?それともどこか根本的にまちがっているのか?
ちなみに、updateをやめてループにしたら問題なく登録されています。
ご教示よろしくお願いします。
以下ソース
'データ読み込み
Do Until READER.EndOfStream
strrec = READER.ReadLine
ret = InStr(strrec, "- -")
ret2 = InStr(strrec, """")
ipp = Trim(Mid(strrec, 1, ret - 1))
hiduke = Trim(Mid(strrec, ret + 5, 20))
accdate = convert_date(hiduke)
nakami = Trim(Mid(strrec, ret2 + 1))
counter = counter + 1
Me.BackgroundWorker1.ReportProgress(counter)
Call table1insert(ip, accdate, nakami)
Call table2insert(ip, accdate, nakami)
'テーブル登録
If counter Mod 1000 = 0 Then
Call t1update()
End If
Loop
Sub table1insert(ByVal ip As String, ByVal accessdate As Date, ByVal url As String)
On Error Resume Next
Dim myROW As DataRow
myROW = dt1.NewRow
myROW("IP_ADDRESS") = ip
myROW("ACCESS_DATE") = accessdate
myROW("URL") = url
dt1.Rows.Add(myROW)
end sub
Sub t1update()
lblregist.Visible = True
lblregist.Text = "table1 処理中"
On Error Resume Next
Dim ip As String
Dim acdate As Date
Dim url As String
Dim i As Integer
もとはda1.update(dt1)だったがうまくいかないのでループにした。
For i = 0 To dt1.Rows.Count - 1
ip = dt1.Rows(i)(0)
acdate = dt1.Rows(i)(1)
url = dt1.Rows(i)(2)
da1.InsertQuery(ip, acdate, url)
Next i
''実アップデート後テーブル行を削除
dt1.Rows.Clear()
End Sub
コピーが抜けてました。ループ後に以下の処理が入ります。なので、1000行以降は登録できないのは当たり前という意見はなしです。
以下ソースループ以降の部分
LOOP
'残り分か1000行未満一括登録
Call t1update()
Call t2update()
よろしくおねがいします。ここは素晴らしい識者の方々が沢山参加しているように思います。{勝手な意見ですが。。)
Until READER.EndOfStreamを止めて、
READER.EndOfStreamの時に変数にフラグをセット
counter Mod 1000 = 0 または、フラグが立っていた場合に登録し
登録後に、フラグが立っていたら、ループを抜ける。
ありがとうございます。
やじゅ様のご指摘のように改変してみましたが、やはりupdateを使うと登録されません。悲しい。。。
おそらく,IDataAdapter.Updateで例外が発生しているのにOn Error Resume Nextで握り潰してしまっているのでしょう。
まずは,
On Error Resume Next
の使用をやめてください。
これにより,何らかの例外が発生していれば,ちゃんとその例外がレポートされるようになります。
IDataAdapter.Updateで例外が発生しているのであれば,その内容を書いてください。
発生していないのであれば,別の原因だと思いますが,ちょっとそこまではわかりません。
YuO様
ありがとうございます。実験してみました。以下の例外は発生しますがこれは、想定内です。
「インデックス、主キー、またはリレーションシップで値が重複しているので、テーブルを変更できませんでした。重複する値のあるフィールドの値を変更するか、インデックスを削除してください。または重複する値を使用できるように再定義してください。」
大量のデータを登録するのでdatateable上オンメモリではもてないので一度登録したらテーブル上のデータを削除してます。なので本来ならdatatableに登録する際にはじかれるべきレコードが残り、update時にはじかれるという仕様です。よろしくお願いします。
> ありがとうございます。実験してみました。以下の例外は発生しますがこれは、想定内です。
想定していながら対処をしていないのですか?
On Error Resume Nextは対処ではなく,握り潰しているだけです。
1003行登録時,最初の実は1000行すら満足に登録されていない,ということはないですか。
Updateメソッドは,結局のところDataRow.RowStateによってInsert/Update/Deleteを順に発行しているに過ぎません。
途中で例外が発生した場合,それ以降の行の処理は行われません。
また,トランザクションすら呼び出し側で処理する必要があります。
テーブルBの方に主キーが存在する行について,
・処理しないのであれば,DataTableから削除しておく
・Updateしたいのであれば,SetModifiedメソッドを呼んでおく
といったことをしてください。
# 1億行のデータって,Accessで扱えるレベルなのだろうか……。
YuO様
>Updateメソッドは,結局のところDataRow.RowStateによって>Insert/Update/Deleteを順に発行しているに過ぎません。
>途中で例外が発生した場合,それ以降の行の処理は行われません。
ありがとうございます。そしたら、例外が出たらそれ以降のupdateは動作しなくなるという認識でよいでしょうか。無視して処理を続けてほしいのですが。でも、そういうことなら、登録されなかった理由がはっきりしました。ありがとうございます。基本的にはデータが多いので登録するしないを簡単に振り分けたいと思い、こんな処理になってしまいました。
また1億行→1000万行の間違いです。重ねてすんません。updateのことがよくわかりました。
> そしたら、例外が出たらそれ以降のupdateは動作しなくなるという認識でよいでしょうか。
よいです。
> 無視して処理を続けてほしいのですが。
IDataAdapterの実装は,エラーが発生した時点で,その後をどうすればよいのかがわかりません。
なので,例外を使ってエラーの発生を呼び出し側に通知し,呼び出し側は対処出来る層が対処することになります。
で,先にも書きましたがトランザクション管理をIDataAdapterの実装は行わないので,成功したところまでがCommitされているという状況になっているわけです。
基本的に,エラーを無視して処理を実行,という考えは捨てた方がよい気がします。
# On Error Resume Nextもそうですが。On Errorとか,今となっては負の遺産としか思えません。
基本的にはエラーが起きないように作り,エラーが起きたら適切な対処を行う,という風にしないと,今回のように原因不明となってしまいます。
今回の場合だと,制約違反が起きないことを先に調べるとか,INSERT時に対処するとか。
YuO様、やじゅ様、お世話になりました。適切なじょげんをありがとうございました。特にYuO様には、お世話になりました。設計から見直してみたいと思います。解決しました。素晴らしすぎます。。
またよろしくお願いします。
'テーブル登録
If counter Mod 1000 = 0 Then
Call t1update()
End If
おかしいよね。1003/1000 =1で余り3件の処理は
しないよね。余り0の時だけ。つまり1000の倍数時のみだよね
do until
loop後にも、余り件数処理しないとダメじゃん?
ツイート | ![]() |