- PR -

C# foreach文について教えてください

1
投稿者投稿内容
ゆきお
常連さん
会議室デビュー日: 2003/12/17
投稿数: 22
お住まい・勤務地: 名古屋市
投稿日時: 2004-04-01 11:34
こんにちは。ゆきおといいます。

C#でOracleDBから2つのテーブルを比較するアプリケーションを作成しています。
コントロールブレーク処理を行い、2つのテーブルを結合させようと考えています。

【結合条件】
・マスタとトランザクションをキーで比較し、一致する場合は結合。
・一致しない場合でもスペースを付与し結合
※両テーブルはキーの昇順でソート済み

【環境】
OS:Windows2000 SP4
Oracle:Oracle9i Release 9.2.0 トライアル版
言語:Visual C#.NET + Oracle Data Provider for .NET(ODP.NET)
.Net Framework v1.0

【C#、ソースコード】
// コントロールブレーク処理をします
private void controlBreak(OracleConnection con , DataSet mstDs , DataSet trnDs)
{
  // マスタ側のループです
  foreach(DataRow mstDr in mstDs.Tables[0].Rows)
  {
    // トランザクション側のループです(@)
    foreach(DataRow trnDr in trnDs.Tables[0].Rows)
    {
      // マスタ.ノード名とトランザクション.ノード名を比較します
      if (String.Compare(mstDr[0].ToString(),trnDr[0].ToString()) == 0)
      {
        // 出力テーブルに書き込みます
        this.insData(con , mstDr , trnDr , 0);
        break;
        
      }
      // 一致しない場合は文字列の大小比較を行う
      else if (String.Compare(mstDr[0].ToString(),trnDr[0].ToString()) < 0)
      {
        // マスタ側が小さい場合はマスタ行のみの出力となる
        this.insData(con , mstDr , trnDr , 1);
        break;
      }
        // マスタ側が大きい場合は何もせずにトランザクションをリードする
    }
  }
}

【問題箇所】
上記コードで処理を行ったところ、プログラムミスが見つかりました。トランザクション側の
ループ(@)では常に開始される行が「trnDs.Tables[0].Rows」の1行目から開始されてしまう点です。この部分はあらかじめfor文で2次元配列に格納し、トランザクション側のループ(@)をfor文に置き換えようと考えています。

【質問】
foreachは必ず最初から始まるのでしょうか?(要素の途中から開始できますか)またこういった2つのテーブルを結合するようなときはどのようなコードで結合しているか教えていただけるとありがたいです。
すなめり
常連さん
会議室デビュー日: 2003/01/29
投稿数: 37
お住まい・勤務地: 横浜
投稿日時: 2004-04-01 14:57
直接の回答ではありませんが。

C#コードでのコントロールブレークではなく、DB上での両テーブルのOUTER JOINではいけませんでしょうか。
一致しない場合はNULLが入りますが、これをスペースに置き換えるのはそう難しくはないと思います。

#もしかして、昔ホスト系の開発をされていた方ですか?
#最近ホスト系に首を突っ込んでいるのですが、似たような処理を良く見かけます。
一郎
ぬし
会議室デビュー日: 2002/10/11
投稿数: 1081
投稿日時: 2004-04-01 15:48
一番良いのはすなめりさんのおっしゃっているOUTER JOIN(外部結合)で表を結合したものを取得する方法です。Oracle9iならできます。
おそらくパフォーマンスが違いだと思います。(どころか二桁かも)

foreach文では必ず最初からだと思います。
どうしてもやるなら、DataRowCollectionクラスはGetEnumerator()メソッドを実装していますので、IEnumeratorオブジェクトを取得できます。
これを使ってコードでMoveNext()しながらDataRowオブジェクトを一行ずつ取得していくという方法になると思います。

もちろんこの方法はお勧めしません。
ゆきお
常連さん
会議室デビュー日: 2003/12/17
投稿数: 22
お住まい・勤務地: 名古屋市
投稿日時: 2004-04-01 17:21
すめなりさん、一郎さんこんにちは。
ゆきおです。

返信ありがとうございます。今時点ではコードを作成し、
動作させることができましたが...

引用:

一郎さんの書き込み (2004-04-01 15:48) より:

foreach文では必ず最初からだと思います。
どうしてもやるなら、DataRowCollectionクラスはGetEnumerator()メソッドを実装していますので、IEnumeratorオブジェクトを取得できます。
これを使ってコードでMoveNext()しながらDataRowオブジェクトを一行ずつ取得していくという方法になると思います。

もちろんこの方法はお勧めしません。



この方法を取ってしまいました。パフォーマンスが悪そうですね。

現状はともかくとして、大事なのはDB上での両テーブルのOUTER JOINですね。あらかじめ結合してればパフォーマンスも良いということですね。この方法に変えると早そうですね。
(そうすると結合のC#コードが全く意味がなくなってしまい、SQL*Plus上でできてしまいそうな気が・・・)

引用:

すなめりさんの書き込み (2004-04-01 14:57) より:

#もしかして、昔ホスト系の開発をされていた方ですか?
#最近ホスト系に首を突っ込んでいるのですが、似たような処理を良く見かけます。



ホスト系の開発はしたことが無いんですよ。ただホスト系はCOBOL言語が多いような気がします。学生のときですがCOBOL言語を使っていました。そのため古いアルゴリズムの発想が出てしまうような気がします。COBOL、C、VB、ときてC#に手を出しています。C#ももう少し理解を深めようとがんばっています。

お二人ともありがとうございました。テーブルをOUTER JOINで結合するSQL文を考えてみます。
参考までに現在のコードを記します。またコメント等あればよろしくお願いします。

【改定後コード】
private void controlBreak(OracleConnection con , DataSet mstDs , DataSet trnDs)
{
  // トランザクション.テーブル用にインデクサを定義します
  IEnumerator trnEnum = trnDs.Tables[0].Rows.GetEnumerator();
  trnEnum.MoveNext();

  int i = 0;
  int trnCnt = trnDs.Tables[0].Rows.Count;

  // マスタ側のループです
  foreach(DataRow mstDr in mstDs.Tables[0].Rows)
  {
    // トランザクション側のループです
    while(i <= trnCnt)
    {
      DataRow trnDr = trnEnum.Current as System.Data.DataRow;

      // マスタ.ノード名とトランザクション.ノード名を比較します
      if (String.Compare(mstDr[0].ToString(),trnDr[0].ToString()) == 0)
      {
        // 出力テーブルに書き込みます
        this.insData(con , mstDr , trnDr , 0);
        // トランザクションをリードします
        trnEnum.MoveNext();
        i++;
        break;
      }
      // 一致しない場合は文字列の大小比較を行う
      else if (String.Compare(mstDr[0].ToString(),trnDr[0].ToString()) < 0)
      {
        // マスタ側が小さい場合はマスタ行のみの出力となる
        this.insData(con , mstDr , trnDr , 1);
        break;
      }
      // マスタ側が大きい場合は何もせずにトランザクションをリードする
      trnEnum.MoveNext();
      i++;
    }
  }
}
1

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