トップ回答者
DataTableの内容を比較するための方法を聞きたいですが

質問
-
// 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の中身を比較して、内容に変化があれば、変化がある部分を出力したいですが、
どんなやり方でやれば良いのかがわかりません。
ご指摘お願いいたします。
回答
-
一つ目も二つ目もデータベースなどから取得したもので、一つ目の取得と二つ目の取得の間にプログラムやその他の方法によって変更された差異を列挙したいという場合であれば、ある程度地道な方法になるのではないかと思います。テーブル自体に変更が加わっているかどうかに関しては、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
すべての返信
-
二つの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
-
一つ目も二つ目もデータベースなどから取得したもので、一つ目の取得と二つ目の取得の間にプログラムやその他の方法によって変更された差異を列挙したいという場合であれば、ある程度地道な方法になるのではないかと思います。テーブル自体に変更が加わっているかどうかに関しては、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
-
2つのDataTableをMergeして、GetChangesで差分を抽出できそうな気がします。試していませんが。
DataTable.Merge メソッド (DataTable, Boolean) (System.Data)
DataTable.GetChanges メソッド () (System.Data)
Blog:プログラマーな日々 http://d.hatena.ne.jp/JHashimoto/ -
皆さん、ありがとうございます。
以下のリンクのソースを参考にしたら、できました。
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(); } } }
-
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/