none
DataTableの内容を比較するための方法を聞きたいですが RRS feed

  • 質問

  •  
          // Round 1
          objectsTableAdapter.Fill(this.dataSet.wk_objects);
          DataTable oldDataTable = this.dataSet_Objects.wk_objects;
    
    
          // Round 2
          objectsTableAdapter.Fill(this.dataSet.wk_objects);
          DataTable newDataTable = this.dataSet_Objects.wk_objects;
    
    
          // 考えている方法1
          // でも、この方法だと、Rows>5000、Columns>20のデータを比較するには、パフォーマンス的に悪そうなので、いい方法はないでしょうか?
          for (int i = 0; i < oldDataTable.Rows.Count; i++)
          {
            for (int k = 0; k < oldDataTable.Columns.Count; k++)
            {
              // 両データテーブルの内容が同じかどうかを比較する。
              bool check = oldDataTable.Rows[i][k] != newDataTable.Rows[i][k];
    
              // 同じくなかったら、その内容を出力する。
              if (check)
              {
                string oldvalue = oldDataTable.Rows[i][k].ToString();
                string newvalue = newDataTable.Rows[i][k].ToString();
    
                Console.WriteLine(oldvalue + " -> " + newvalue);
              }
            }
          }

    二つのDataTableの中身を比較して、内容に変化があれば、変化がある部分を出力したいですが、

     

    どんなやり方でやれば良いのかがわかりません。

    ご指摘お願いいたします。

    2011年1月21日 6:38

回答

  • 「変化がある部分を出力したい」の"変化がある部分"が項目ごとであれば
    この方法しかないんじゃないでしょうか。
    私も同じ方法ですると思います。
    すごくまれにしか違いがないなら、行ごとの比較をして違っていた場合だけ
    その行のそれぞれの項目についてチェックすることもあるかもしれません。
    # でも普通はそっちのほうが遅そう
    • 回答としてマーク Pinokio-k 2011年1月21日 9:11
    2011年1月21日 7:13
  • 一つ目も二つ目もデータベースなどから取得したもので、一つ目の取得と
    二つ目の取得の間にプログラムやその他の方法によって変更された
    差異を列挙したいという場合であれば、ある程度地道な方法になるのでは
    ないかと思います。
    テーブル自体に変更が加わっているかどうかに関しては、SequenceEqual
    を利用することで可能のようですが。
    ※2つの要素の集合のすべてを要素の位置も含めて同一化を調べます。

    また、御提示されているコードですと、行の削除や追加が行われている場合は、
    その行以降は全て比較がズレていってしまいます。ちょっと考えてみましたが、
    まずは、比較をする前に、新旧のテーブルを、テーブルキー値のグループをKeyとし
    DataRowをValueとするようなDictionaryなどに展開します。
    その後2つのDictionaryに対して、同一Keyのデータ有無をチェックし
    同一Keyがなければ、それは追加もしくは削除行ですし、同一Keyがあれば、
    そのValueのDataRowに対してDataRowComparerで変更有無をチェックし、
    変更があればそのRowの中身自体を全てチェックして変更項目を取得するような
    感じかな~と思いました。

    行の削除や追加はなく、行の順序も変わってない場合であれば、
    Row数分ループし、DataRowComparerを行い、異なっている場合のみ詳細な差異を
    取得すればいいかなと思います。

    もしもFramework4.0であれば並列処理が可能なので、ループ処理に関して
    実行するPCによっては処理速度を向上できる場合があります。
    • 回答としてマーク Pinokio-k 2011年1月21日 9:11
    2011年1月21日 7:53

すべての返信

  • 「変化がある部分を出力したい」の"変化がある部分"が項目ごとであれば
    この方法しかないんじゃないでしょうか。
    私も同じ方法ですると思います。
    すごくまれにしか違いがないなら、行ごとの比較をして違っていた場合だけ
    その行のそれぞれの項目についてチェックすることもあるかもしれません。
    # でも普通はそっちのほうが遅そう
    • 回答としてマーク Pinokio-k 2011年1月21日 9:11
    2011年1月21日 7:13
  • 二つのDataTableはどのようにして得たものでしょうか。

    もし、一つ目はデータベースから取得したもので、二つ目はそれの値を変更したものであれば、

    DataTable.Select メソッドというものがあります。

    http://msdn.microsoft.com/ja-jp/library/b5c0xc84(v=VS.100).aspx

    http://msdn.microsoft.com/ja-jp/library/system.data.dataviewrowstate(v=VS.100).aspx

    • 回答の候補に設定 J.Hashimoto 2011年1月21日 9:40
    2011年1月21日 7:19
  • 一つ目も二つ目もデータベースなどから取得したもので、一つ目の取得と
    二つ目の取得の間にプログラムやその他の方法によって変更された
    差異を列挙したいという場合であれば、ある程度地道な方法になるのでは
    ないかと思います。
    テーブル自体に変更が加わっているかどうかに関しては、SequenceEqual
    を利用することで可能のようですが。
    ※2つの要素の集合のすべてを要素の位置も含めて同一化を調べます。

    また、御提示されているコードですと、行の削除や追加が行われている場合は、
    その行以降は全て比較がズレていってしまいます。ちょっと考えてみましたが、
    まずは、比較をする前に、新旧のテーブルを、テーブルキー値のグループをKeyとし
    DataRowをValueとするようなDictionaryなどに展開します。
    その後2つのDictionaryに対して、同一Keyのデータ有無をチェックし
    同一Keyがなければ、それは追加もしくは削除行ですし、同一Keyがあれば、
    そのValueのDataRowに対してDataRowComparerで変更有無をチェックし、
    変更があればそのRowの中身自体を全てチェックして変更項目を取得するような
    感じかな~と思いました。

    行の削除や追加はなく、行の順序も変わってない場合であれば、
    Row数分ループし、DataRowComparerを行い、異なっている場合のみ詳細な差異を
    取得すればいいかなと思います。

    もしもFramework4.0であれば並列処理が可能なので、ループ処理に関して
    実行するPCによっては処理速度を向上できる場合があります。
    • 回答としてマーク Pinokio-k 2011年1月21日 9:11
    2011年1月21日 7:53
  • 2つのDataTableをMergeして、GetChangesで差分を抽出できそうな気がします。試していませんが。

    DataTable.Merge メソッド (DataTable, Boolean) (System.Data)

    DataTable.GetChanges メソッド () (System.Data)

     


    Blog:プログラマーな日々 http://d.hatena.ne.jp/JHashimoto/
    2011年1月21日 10:20
  • 皆さん、ありがとうございます。

    以下のリンクのソースを参考にしたら、できました。

    http://d.hatena.ne.jp/gsf_zero1/20090823/p1

    参考したソースも張っておきます。

     

     

    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Linq;
    
    namespace DataRowComparerSample {
    
      class Program {
    
        public void Execute() {
          //
          // 2つのデータテーブルを作成.
          //
          DataTable table1 = new DataTable();
          DataTable table2 = new DataTable();
    
          InitializeColumns(table1);
          InitializeColumns(table2);
    
          FillRows(table1);
          FillRows(table2);
    
          ////////////////////////////////////////////////////////
          //
          // 各行データを比較.
          //
          // SequenceEqualを利用して全行一発比較.
          IEnumerable<DataRow> enumerable1 = table1.AsEnumerable();
          IEnumerable<DataRow> enumerable2 = table2.AsEnumerable();
    
          bool isAllRowEquals = enumerable1.SequenceEqual(enumerable2, DataRowComparer<DataRow>.Default);
    
          Console.WriteLine("全行のデータが同じ? => {0}", isAllRowEquals);
    
          //
          // 一行ずつ比較.
          //
          DataRowComparer<DataRow> comparer = DataRowComparer<DataRow>.Default;
          List<bool>        results = new List<bool>();
          for(int i = 0; i < table1.Rows.Count; i++) {
            results.Add(comparer.Equals(table1.Rows[i], table2.Rows[i]));
          }
    
          Console.WriteLine("各行のデータが同じ? => {0}", results.All( (isEqual) => { return isEqual; }));
        }
    
        #region Helper Methods
        protected void InitializeColumns(DataTable table) {
    
          table.Columns.Add("Id");
          table.Columns.Add("Name");
          table.Columns.Add("Age", typeof(int));
        }
    
        protected void FillRows(DataTable table) {
    
          try {
            table.BeginLoadData();
    
            for(int i = 0; i < 10; i++) {
              table.LoadDataRow(new object[] { i, string.Format("name-{0}", i), (i + 10) }, true);
            }
          } finally {
            table.EndLoadData();
          }
        }
        #endregion
    
        static void Main(string[] args) {
          (new Program()).Execute();
    
          Console.WriteLine(string.Empty);
          Console.WriteLine("Press Any Key to Exit....");
          Console.Read();
        }
      }
    }
    
    

    2011年1月21日 12:45
  • Exceptメソッドを使って、以下のようにしてもできるんじゃないかと思います。

    var  exceptRows = enumerable1.Except(enumerable2, DataRowComparer.Default);

    Enumerable.Except メソッド
    http://msdn.microsoft.com/ja-jp/library/system.linq.enumerable.except.aspx

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年1月21日 16:13
    モデレータ