- PR -

OracleDataReaderでの処理時間について

投稿者投稿内容
extream
ベテラン
会議室デビュー日: 2005/12/26
投稿数: 83
投稿日時: 2007-02-13 17:41
- 開発環境 -
Oracle 10g(リリース1)
Windows XP SP2
VS2003
- - - - - - -

OracleDataReaderを用いてコーディングしているのですが、疑問が生じました。
わかる方いたら教えてください。m(__)m

DBデータ取得処理を作成したのですが、やたら処理時間がかかっているので、ログをとったところ、以下の処理を行った場合
 ・time_While
 ・time_CSV
で、処理時間に大きな差が生じていました。

実際には、10000件程度のデータを処理対象とした場合、
 ・time_While … 0分6秒828
 ・time_CSV  … 0分1秒875
となっていました。

OracleDataReaderを使う場合、多少の時間ロスは仕方ないのでしょうか??
なお、今回計ったログではロス時間は5秒程度ですが、以前同様のログをとった場合は5分くらい不明な時間がありました。。。
これはOracleの状態に左右されているのでしょうか??
詳しい方いたら、ぜひ教えてくださいm(__)m



※ ・1回のSQLで取得するデータは最大で800件
   (select ○○ from XX WHERE A=1 or A=2 or A=3 …)
  ・OUTER JOIN等は行っていません。(1つの表のみ対象)
  ・以下の関数を、複数回コールしています。


-----------------------------------------------------------

public TimeSpan time_CSV;
public TimeSpan time_While;

public void GetData( string sql )
{
OracleCommand oraCmd = new OracleCommand();
oraCmd.CommandText = sql; // SQLをセット
oraCmd.Connection = This.oraCnn; // コネクションをセット

OracleDataReader reader = oraCmd.ExecuteReader();

// --------------------
// Readerで1行づつ処理
// --------------------
DateTime dtStartWhile = DateTime.Now;
while ( reader.Read() )
{
DateTime dtStartcsv = DateTime.Now;

// ----------
// CSV出力
// ----------
StringBuilder line = new StringBuilder();
line.Append( reader[0] );
line.Append( "," );
line.Append( reader[1] );
line.Append( "," );
line.Append( reader[2] );
line.Append( "," );
line.Append( reader[3] );

writer_Csv.WriteLine( line.ToString() );

DateTime dtEndcsv = DateTime.Now;
time_CSV += (dtEndcsv - dtStartcsv);
}
DateTime dtEndWhile = DateTime.Now;
time_While += (dtEndWhile - dtStartWhile);
}

-----------------------------------------------------------
masa
大ベテラン
会議室デビュー日: 2004/10/28
投稿数: 161
投稿日時: 2007-02-13 18:01
データベースとの通信はインフラに依存しますので、
何かと比較しないと妥当なのかどうかはわかりません。

ところで、
OracleDataReader は ODP.NET のものでしょうか。
.NET 標準クラスライブラリのものでしょうか。


・OleDb や ODP.NET など他のプロバイダを使用したときとの比較

・データベースサーバーへの ping

・5分かかるときもあるとのことですが「波」があるのかどうか。
 ほかのPCから何かを行っていて負荷が高いということもありえますよね。

などを計測してみてはどうでしょうか。


追記

> line.Append( reader[0] );
> line.Append( "," );
> line.Append( reader[1] );
> line.Append( "," );
> line.Append( reader[2] );
> line.Append( "," );
> line.Append( reader[3] );

データベースへの通信回数を減らす方法の1つに、
reader.GetValues() を使ってまとめて値を取得してしまうというものもあります。
大して変わらないかもしれませんが・・。

// ループの外で格納用配列を確保
byte[] buf = new byte[reader.FieldCount];

// 値をまとめて取得
reader.GetValues(buf);

line.Append( buf[0] );

for ( int i = 1; i < buf.Length; i++ ) {
 line.Append( "," );
 line.Append( buf[i] );
}


[ メッセージ編集済み 編集者: masa 編集日時 2007-02-13 18:07 ]

[ メッセージ編集済み 編集者: masa 編集日時 2007-02-13 18:09 ]
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2007-02-13 18:22
本題とは無関係ですが…
引用:

extreamさんの書き込み (2007-02-13 17:41) より:
// ----------
// CSV出力
// ----------
StringBuilder line = new StringBuilder();
line.Append( reader[0] );
line.Append( "," );
line.Append( reader[1] );
line.Append( "," );
line.Append( reader[2] );
line.Append( "," );
line.Append( reader[3] );

writer_Csv.WriteLine( line.ToString() );


こういうStringBuilderの使い方はあまり意味はありません。
まあ、列ごとにさらにループするとかならまた話は別ですが。

別に悪い方法というわけではないですが、もしパフォーマンスアップのため
という理由なら実は意味のないことをしています。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2007-02-13 18:37
五秒というと、最初の接続かなぁ?
StringBuilder.Length とか、AppendFormat とか
_________________
ぽぴ王子
ぬし
会議室デビュー日: 2006/03/24
投稿数: 475
お住まい・勤務地: お住まい:城・勤務地:城
投稿日時: 2007-02-13 19:20
なちゃさんに便乗して、これまた本題とは無関係ですが。
こんな感じはどうでしょうかね。

コード:

List<string> buff = new List<string>();
for(int i=0; i<reader.FieldCount; i++)
{
    buff.Add(reader[i].ToString());
}
writer_Csv.WriteLine(string.Join(",", buff));


あまりエレガントな方法ではないですけど、まぁとりあえず。
masa さんの方法と組み合わせるといいかもです。

# StringBuilder.Append() って意外と遅いらしいです。
_________________
ぽぴ王子@わんくま同盟
ぽぴ王子の人生プログラミング中 / ぽぴンち。
extream
ベテラン
会議室デビュー日: 2005/12/26
投稿数: 83
投稿日時: 2007-02-13 21:17
みなさん、回答ありがとうございます。

OracleDataReader は ODP.NETのものです。

stringを用いて連結させるより、StringBuilder.Append() を用いた方が早いといろいろなサイトでみたのでやっていたのですが、そんなに変わらないんですか。。。
C#やり始めてから癖になってしまいました。

なお、
while ( reader.Read() )
{
  …
}
で、5分くらい処理時間がかかっていたときは、800件分のデータを取得してループ処理する際、620〜630件目の処理で10〜20秒程度かかっていました。
8000件程度の処理をしていたので、例の関数を10回程度呼ぶのですが、全てで620〜630件目で時間がかかっていました。
(「1〜620、630〜800」は、トータルで1秒程度です。)

なお、件数が問題かと思ったので、1回で処理する件数を500件にしたのですが、500〜600回目のどこかですごく時間がかかっていました。

Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2007-02-13 23:25
ほい(^_^;)つ http://blogs.wankuma.com/rti/archive/2007/01/08/55000.aspx

引用:

コード:
DateTime dtStartWhile = DateTime.Now; 
while ( reader.Read() ) 
{ 
    DateTime dtStartcsv = DateTime.Now; 
    ...
    DateTime dtEndcsv = DateTime.Now; 
    time_CSV += (dtEndcsv - dtStartcsv); 
} 
DateTime dtEndWhile = DateTime.Now; 
time_While += (dtEndWhile - dtStartWhile); 



なんか、変でない?time_CSV は、最後の読み込みの時にしか時間が計られ無くない?
_________________
masa
大ベテラン
会議室デビュー日: 2004/10/28
投稿数: 161
投稿日時: 2007-02-14 09:04
引用:

stringを用いて連結させるより、StringBuilder.Append() を用いた方が早いといろいろなサイトでみたのでやっていたのですが、そんなに変わらないんですか。。。



まったく意味がないわけではないと思いますよ。連結回数やその長さによって効果は異なるのではないでしょうか。

回数・長さが小さければ、StringBuilder の生成+Append のコストと変わらないことがあります。よく見る StringBuilder の検証コードでは「何万回連結したら」というようなものが多いですよね。実際のアプリケーションではそういった場面が少ないので、効果は感じられないということもいえると思います。

今回の例では、生成はループの外で行ってループの先頭で Length = 0 とするのが一応セオリーとされています。
また、StringBuilder は格納領域が不足したときに倍に拡張しますから、
適当なサイズがわかっていればサイズを指定しておいたほうがよいとも聞きます。

スキルアップ/キャリアアップ(JOB@IT)