アクセスの楽観的updateメソッドについて

解決


ぽんきち  2008-10-24 08:27:28  No: 145347

いつもお世話になっております。
表題のとおり、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


ぽんきち  2008-10-24 08:32:18  No: 145348

コピーが抜けてました。ループ後に以下の処理が入ります。なので、1000行以降は登録できないのは当たり前という意見はなしです。
以下ソースループ以降の部分
LOOP

 '残り分か1000行未満一括登録
        Call t1update()
        Call t2update()

よろしくおねがいします。ここは素晴らしい識者の方々が沢山参加しているように思います。{勝手な意見ですが。。)


やじゅ  2008-10-24 22:02:03  No: 145349

Until READER.EndOfStreamを止めて、
READER.EndOfStreamの時に変数にフラグをセット
counter Mod 1000 = 0 または、フラグが立っていた場合に登録し
登録後に、フラグが立っていたら、ループを抜ける。


ぽんきち  2008-10-25 00:36:45  No: 145350

ありがとうございます。
やじゅ様のご指摘のように改変してみましたが、やはりupdateを使うと登録されません。悲しい。。。


YuO  2008-10-27 11:38:02  No: 145351

おそらく,IDataAdapter.Updateで例外が発生しているのにOn Error Resume Nextで握り潰してしまっているのでしょう。

まずは,
On Error Resume Next
の使用をやめてください。

これにより,何らかの例外が発生していれば,ちゃんとその例外がレポートされるようになります。
IDataAdapter.Updateで例外が発生しているのであれば,その内容を書いてください。
発生していないのであれば,別の原因だと思いますが,ちょっとそこまではわかりません。


ぽんきち  2008-10-27 21:57:14  No: 145352

YuO様
ありがとうございます。実験してみました。以下の例外は発生しますがこれは、想定内です。
「インデックス、主キー、またはリレーションシップで値が重複しているので、テーブルを変更できませんでした。重複する値のあるフィールドの値を変更するか、インデックスを削除してください。または重複する値を使用できるように再定義してください。」
大量のデータを登録するのでdatateable上オンメモリではもてないので一度登録したらテーブル上のデータを削除してます。なので本来ならdatatableに登録する際にはじかれるべきレコードが残り、update時にはじかれるという仕様です。よろしくお願いします。


YuO  2008-10-27 22:52:46  No: 145353

> ありがとうございます。実験してみました。以下の例外は発生しますがこれは、想定内です。

想定していながら対処をしていないのですか?
On Error Resume Nextは対処ではなく,握り潰しているだけです。
1003行登録時,最初の実は1000行すら満足に登録されていない,ということはないですか。

Updateメソッドは,結局のところDataRow.RowStateによってInsert/Update/Deleteを順に発行しているに過ぎません。
途中で例外が発生した場合,それ以降の行の処理は行われません。
また,トランザクションすら呼び出し側で処理する必要があります。

テーブルBの方に主キーが存在する行について,
・処理しないのであれば,DataTableから削除しておく
・Updateしたいのであれば,SetModifiedメソッドを呼んでおく
といったことをしてください。
# 1億行のデータって,Accessで扱えるレベルなのだろうか……。


ぽんきち  2008-10-28 00:29:00  No: 145354

YuO様
>Updateメソッドは,結局のところDataRow.RowStateによって>Insert/Update/Deleteを順に発行しているに過ぎません。
>途中で例外が発生した場合,それ以降の行の処理は行われません。

ありがとうございます。そしたら、例外が出たらそれ以降のupdateは動作しなくなるという認識でよいでしょうか。無視して処理を続けてほしいのですが。でも、そういうことなら、登録されなかった理由がはっきりしました。ありがとうございます。基本的にはデータが多いので登録するしないを簡単に振り分けたいと思い、こんな処理になってしまいました。
また1億行→1000万行の間違いです。重ねてすんません。updateのことがよくわかりました。


YuO  2008-10-28 01:43:16  No: 145355

> そしたら、例外が出たらそれ以降のupdateは動作しなくなるという認識でよいでしょうか。

よいです。

> 無視して処理を続けてほしいのですが。

IDataAdapterの実装は,エラーが発生した時点で,その後をどうすればよいのかがわかりません。
なので,例外を使ってエラーの発生を呼び出し側に通知し,呼び出し側は対処出来る層が対処することになります。

で,先にも書きましたがトランザクション管理をIDataAdapterの実装は行わないので,成功したところまでがCommitされているという状況になっているわけです。

基本的に,エラーを無視して処理を実行,という考えは捨てた方がよい気がします。
# On Error Resume Nextもそうですが。On Errorとか,今となっては負の遺産としか思えません。

基本的にはエラーが起きないように作り,エラーが起きたら適切な対処を行う,という風にしないと,今回のように原因不明となってしまいます。
今回の場合だと,制約違反が起きないことを先に調べるとか,INSERT時に対処するとか。


ぽんきち  2008-10-28 07:13:07  No: 145356

YuO様、やじゅ様、お世話になりました。適切なじょげんをありがとうございました。特にYuO様には、お世話になりました。設計から見直してみたいと思います。解決しました。素晴らしすぎます。。
またよろしくお願いします。


  2008-10-28 07:18:12  No: 145357

'テーブル登録
 If counter Mod 1000 = 0 Then
    Call t1update()
 End If
おかしいよね。1003/1000  =1で余り3件の処理は
しないよね。余り0の時だけ。つまり1000の倍数時のみだよね
do until
loop後にも、余り件数処理しないとダメじゃん?


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加