- - PR -
ADO.NETで更新処理が遅い
投稿者 | 投稿内容 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2004-05-18 00:39
みなさんアドバイスありがとうございます。
今 SqlCommandでINSERT文を発行する方法でプログラムの改造を進めています。 簡単にテストしたところ少し速くなるようです。 もう少しまとまった結果が出たらこのスレッドでお知らせしたいと 思っています。 >「リンクサーバー」というらしいです。 わざわざ調べてくださって感謝します。 ただ、この機能は今回のドライバが対応していない可能性があるので 現在保留中です。 >「適当な列を同じ値で書き換え、RowStateを強制的にModifiedにし、UpdateCommand.CommandTextにINSERT文を書く」 私にはこれがどのような方法なのかわかりません。 「適当な列を同じ値で書き換え」の部分が分かりません。 結局のところ、 ODBCDataReader(ODBC側)からDataSet(SQL Server側)に レコードをセットする処理は必要になるのですよね? ODBC側をDataSetで拾ってきて、それのRowStateをModifiedにしても それによって更新されるのはODBC側のデータベースと思えます。 >数万件程度でこれが遅くなるとは信じがたいです。 タスクマネージャを見ながら処理を実行すると 12000件くらいごとに使用メモリ(PF使用量)が20MBくらい ずつ増えていくようです。 多分、確保したバッファが足りなくなって新たなバッファを 確保しに行っていると思うのですが、その動作が遅さの原因では ないかとにらんでいます。 なお、このテーブルの項目数は40程度です。 | ||||||||||||
|
投稿日時: 2004-05-18 09:18
未検証なので、実際にどうなるかわかりませんが、、、 Dim ds = New DataSet(〜) Dim SelectCommand = New OdbcCommand("SELECT 〜", ODBCへの接続) Dim InsertCommand = New SqlCommand("INSERT INTO 〜", Sql Serverへの接続) Dim OdbcAdapter = New OdbcDataAdapter(〜) Dim SqlAdapter = New SqlDataAdapter(〜) ' データを取り込む OdbcAdapter.SelectCommand = SelectCommand OdbcAdapter.Fill(ds) ' 強制modified Dim row As DataRow Dim anzensaku as Object For Each row In ds.Tables(0).Rows row(0) = row(0) ' これが「同じ値で書き換える」 'anzensaku = row(0) 'row(0) = 何か 'row(0).AcceptChanges() 'row(0) = anzensaku ' これでModifiedになるでしょう ^^; Next SqlAdapter.UpdateCommand = InsertCommand SqlAdapter.Update(ds.Tables(0)) こんな感じ。 取ってきたDataSetは、物理的にデータベースとは切れています。それを利用します。書き出す為のUpdateで使う接続は、SQL Server宛の接続です。そして、RowStateがModifiedならばUpdateCommandが適用されますが、SQL Server内には当然その行はありませんから、UpdateCommandにINSERT文を書きます。 強制Modifiedは、イコールの左辺と右辺を同じ値にします。もしかしたらオプティマイザが命令を削除してしまうかもしれないので、anzensakuのように、いったん別の値を入れて、元の値で上書きします。Originとの差異でRowStateを決めていたら、別の値で上書きした直後にAcceptChangesメソッドをコールして、変更を確定します。 | ||||||||||||
|
投稿日時: 2004-05-19 09:33
[ メッセージ編集済み 編集者: 未記入 編集日時 2007-01-19 21:58 ] | ||||||||||||
|
投稿日時: 2004-05-19 10:48
よく考えたら、別のデータベースへのアクセスなんだから、接続2つ作るほうがよい?読み込んだデータをDataSetにストアするのではなく、そのままSqlCommandのパラメータとしてSQL Serverに渡してしまう、と。メモリの拡張確保をしないし、ループ回るのも1回だけなので、かなり改善されると思います。
| ||||||||||||
|
投稿日時: 2004-05-20 17:12
Jittaさま、ぜうすさま返信ありがとうございます。
現在、ループの中で SQL Insert文を直接発行する方法で開発を終了しました。 今回のケースでは SQL Insert文の使用はSqlDataAdapter.Updateを使用するより 約25%の時間短縮になりました。 ここでいったんまとめておきます。 このスレッドでは話題にしなかったことも関連がありそうなことで 今回わかったことはまとめに含みます。 ■まとめ ●SqlDataAdapter.Updateメソッドは遅い SqlCommandでINSERT文を直接サーバーに投げる方が早い。 ●DataSetを使うとメモリを大量消費する テーブル設計にもよるが対象が数十万件以上の場合にはまず実用性がない。 ●レコードの検索はDataTable.Rows.Findメソッドがかなり速い。 sqlCommandでSELECT文を発行してレコードを検索するのは遅い。 (レコードが既に登録されているか否かのチェック方法の話です) ●レコードを更新する必要がない場合はDataReaderを使う 異論・補足等あるかたはご意見伺わせてください。 ■ さて、とりあえず我慢できる速さで動くようになりましたので ここからは今後の参考のためと興味のために伺うのですが >SqlAdapter.Update(ds.Tables(0)) この方法は理解しました。 ためしてはいませんが、大変興味深いと思っています。 しかし、今度は >よく考えたら、別のデータベースへのアクセスなんだから、接続2つ作るほうがよい? これが理解できません…。 もともと接続は2つありますよね? DataSetにストアしないで渡すことなどできるのでしょうか? これはさらによさそうな方法のようですが、 もう少し具体的に教えていただけませんか? >テーブルAからCSVを作成し、SQLServerのインポートを利用してテーブルBへ登録する方法は...? これはハードディスクへのアクセスが約2倍になりますよね。 CSVに落とした後は速いのかも知れませんが、 今回のケースではCSVに落とすところがかえって遅くなりそうなので この方法は見送りました。 提案ありがとうございます。 速度向上のためにアドバイスしてくださったみなさんに改めてお礼を言います。 ありがとうございます! | ||||||||||||
|
投稿日時: 2004-05-20 17:45
意図は、たぶん、これと同じ。
擬似コード:
|