【问题标题】:How to compare 2 dataTables如何比较 2 个数据表
【发布时间】:2011-11-22 23:54:05
【问题描述】:

我有 2 个数据表,我只想知道它们是否相同。通过“相同”,我的意思是它们是否具有完全相同的行数,每列中的数据完全相同,或者没有。我很想写(找到)一个接受两个表并返回布尔值的方法。

如何以这种方式比较 2 个数据表?两者都有相同的架构。

【问题讨论】:

  • 这里有人问过这个问题:stackoverflow.com/questions/164144/… 这个问题并不完全相同。在您的情况下,您需要遍历行,并在该循环中,遍历行中的列以比较值。
  • @DavidStratton -对不起,这不是同一个问题。
  • 这样做的目的是什么?
  • 我知道这就是我没有投票关闭的原因。我编辑了我的评论。这篇文章展示了如何做到这一点,遵循我编辑的评论的一般建议:canlu.blogspot.com/2009/05/…
  • @CodeBlend - 我将原始表与同一张表的可能修改版本进行比较。如果什么都没有改变,我想打印一份报告,如果有什么改变,我想更新值然后打印报告。

标签: c# .net datatable


【解决方案1】:
 public static bool AreTablesTheSame( DataTable tbl1, DataTable tbl2)
 {
    if (tbl1.Rows.Count != tbl2.Rows.Count || tbl1.Columns.Count != tbl2.Columns.Count)
                return false;


    for ( int i = 0; i < tbl1.Rows.Count; i++)
    {
        for ( int c = 0; c < tbl1.Columns.Count; c++)
        {
            if (!Equals(tbl1.Rows[i][c] ,tbl2.Rows[i][c]))
                        return false;
        }
     }
     return true;
  }

【讨论】:

  • -是的,我明白这是如何工作的。我可能会看看它是否比其他选项更快。比萨时间!
  • 代码不可编译,也应该使用!Equals而不是'!='操作符。见帖子geekswithblogs.net/mnf/archive/2013/08/27/…
【解决方案2】:

如果您将 DataTable 作为函数返回,您可以:

DataTable dataTable1; // Load with data
DataTable dataTable2; // Load with data (same schema)

// Fast check for row count equality.
if ( dataTable1.Rows.Count != dataTable2.Rows.Count) {
    return true;
}

var differences =
    dataTable1.AsEnumerable().Except(dataTable2.AsEnumerable(),
                                            DataRowComparer.Default);

return differences.Any() ? differences.CopyToDataTable() : new DataTable();

【讨论】:

  • 还不错,这个
  • 但是当您从表中删除一行时,差异不会显示出来。嗯!
  • @netfed 我通过检查更新了答案,以确保不会发生这种情况。
【解决方案3】:

您需要遍历每个表的行,然后遍历该循环中的每一列来比较各个值。

这里有一个代码示例:http://canlu.blogspot.com/2009/05/how-to-compare-two-datatables-in-adonet.html

【讨论】:

  • -我使用它并编写了一个公共方法来调用代码并返回布尔值。使用的代码见编辑。
【解决方案4】:

OP MAW74656 最初在问题正文中发布了此答案,以响应accepted answer,如this comment 中所述:

我使用它并编写了一个公共方法来调用代码并返回布尔值。

OP 的回答:

使用的代码:

public bool tablesAreTheSame(DataTable table1, DataTable table2)
{
    DataTable dt;
    dt = getDifferentRecords(table1, table2);

    if (dt.Rows.Count == 0)
        return true;
    else
        return false;
}

//Found at http://canlu.blogspot.com/2009/05/how-to-compare-two-datatables-in-adonet.html
private DataTable getDifferentRecords(DataTable FirstDataTable, DataTable SecondDataTable)
{
    //Create Empty Table     
    DataTable ResultDataTable = new DataTable("ResultDataTable");

    //use a Dataset to make use of a DataRelation object     
    using (DataSet ds = new DataSet())
    {
        //Add tables     
        ds.Tables.AddRange(new DataTable[] { FirstDataTable.Copy(), SecondDataTable.Copy() });

        //Get Columns for DataRelation     
        DataColumn[] firstColumns = new DataColumn[ds.Tables[0].Columns.Count];
        for (int i = 0; i < firstColumns.Length; i++)
        {
            firstColumns[i] = ds.Tables[0].Columns[i];
        }

        DataColumn[] secondColumns = new DataColumn[ds.Tables[1].Columns.Count];
        for (int i = 0; i < secondColumns.Length; i++)
        {
            secondColumns[i] = ds.Tables[1].Columns[i];
        }

        //Create DataRelation     
        DataRelation r1 = new DataRelation(string.Empty, firstColumns, secondColumns, false);
        ds.Relations.Add(r1);

        DataRelation r2 = new DataRelation(string.Empty, secondColumns, firstColumns, false);
        ds.Relations.Add(r2);

        //Create columns for return table     
        for (int i = 0; i < FirstDataTable.Columns.Count; i++)
        {
            ResultDataTable.Columns.Add(FirstDataTable.Columns[i].ColumnName, FirstDataTable.Columns[i].DataType);
        }

        //If FirstDataTable Row not in SecondDataTable, Add to ResultDataTable.     
        ResultDataTable.BeginLoadData();
        foreach (DataRow parentrow in ds.Tables[0].Rows)
        {
            DataRow[] childrows = parentrow.GetChildRows(r1);
            if (childrows == null || childrows.Length == 0)
                ResultDataTable.LoadDataRow(parentrow.ItemArray, true);
        }

        //If SecondDataTable Row not in FirstDataTable, Add to ResultDataTable.     
        foreach (DataRow parentrow in ds.Tables[1].Rows)
        {
            DataRow[] childrows = parentrow.GetChildRows(r2);
            if (childrows == null || childrows.Length == 0)
                ResultDataTable.LoadDataRow(parentrow.ItemArray, true);
        }
        ResultDataTable.EndLoadData();
    }

    return ResultDataTable;
}

【讨论】:

  • 我试过了,它在 DataRelation 中最多显示 32 列。
【解决方案5】:

尝试使用 linq to Dataset

(from b in table1.AsEnumerable()  
    select new { id = b.Field<int>("id")}).Except(
         from a in table2.AsEnumerable() 
             select new {id = a.Field<int>("id")})

查看这篇文章:Comparing DataSets using LINQ

【讨论】:

  • -我没有在问题中指定这一点(因此是 +1),但我想避免使用 LINQ,以便可以在带有 .NET 2.0 的工作站上运行。是的,它很蹩脚,但它是一种要求。
【解决方案6】:
    /// <summary>
    /// https://stackoverflow.com/a/45620698/2390270
    /// Compare a source and target datatables and return the row that are the same, different, added, and removed
    /// </summary>
    /// <param name="dtOld">DataTable to compare</param>
    /// <param name="dtNew">DataTable to compare to dtOld</param>
    /// <param name="dtSame">DataTable that would give you the common rows in both</param>
    /// <param name="dtDifferences">DataTable that would give you the difference</param>
    /// <param name="dtAdded">DataTable that would give you the rows added going from dtOld to dtNew</param>
    /// <param name="dtRemoved">DataTable that would give you the rows removed going from dtOld to dtNew</param>
    public static void GetTableDiff(DataTable dtOld, DataTable dtNew, ref DataTable dtSame, ref DataTable dtDifferences, ref DataTable dtAdded, ref DataTable dtRemoved)
    {
        try
        {
            dtAdded = dtOld.Clone();
            dtAdded.Clear();
            dtRemoved = dtOld.Clone();
            dtRemoved.Clear();
            dtSame = dtOld.Clone();
            dtSame.Clear();
            if (dtNew.Rows.Count > 0) dtDifferences.Merge(dtNew.AsEnumerable().Except(dtOld.AsEnumerable(), DataRowComparer.Default).CopyToDataTable<DataRow>());
            if (dtOld.Rows.Count > 0) dtDifferences.Merge(dtOld.AsEnumerable().Except(dtNew.AsEnumerable(), DataRowComparer.Default).CopyToDataTable<DataRow>());
            if (dtOld.Rows.Count > 0 && dtNew.Rows.Count > 0) dtSame = dtOld.AsEnumerable().Intersect(dtNew.AsEnumerable(), DataRowComparer.Default).CopyToDataTable<DataRow>();
            foreach (DataRow row in dtDifferences.Rows)
            {
                if (dtOld.AsEnumerable().Any(r => Enumerable.SequenceEqual(r.ItemArray, row.ItemArray))
                    && !dtNew.AsEnumerable().Any(r => Enumerable.SequenceEqual(r.ItemArray, row.ItemArray)))
                {
                    dtRemoved.Rows.Add(row.ItemArray);
                }
                else if (dtNew.AsEnumerable().Any(r => Enumerable.SequenceEqual(r.ItemArray, row.ItemArray))
                    && !dtOld.AsEnumerable().Any(r => Enumerable.SequenceEqual(r.ItemArray, row.ItemArray)))
                {
                    dtAdded.Rows.Add(row.ItemArray);
                }
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.ToString());
        }
    }

【讨论】:

    【解决方案7】:

    没有什么可以为你做这件事;完成此操作的唯一方法是迭代所有行/列并将它们相互比较。

    【讨论】:

    • -希望 .NET 的下一个版本将包含一个 DataTable.CompareTo(DataTable) 方法来为我们解决这个问题。
    【解决方案8】:

    好吧,如果您完全使用 DataTable,而不是比较两个“DataTables”,您是否可以将在加载 AKA DataTable.GetChanges Method (DataRowState) 时将与原始数据发生更改的 DataTable 进行比较

    【讨论】:

      【解决方案9】:

      或者这个,我没有实现数组比较,所以你也会有一些乐趣:)

      public bool CompareTables(DataTable a, DataTable b)
      {
          if(a.Rows.Count != b.Rows.Count)
          {
              // different size means different tables
              return false;
          }
      
          for(int rowIndex=0; rowIndex<a.Rows.Count; ++rowIndex)
          {
              if(!arraysHaveSameContent(a.Rows[rowIndex].ItemArray, b.Rows[rowIndex].ItemArray,))
              {
                  return false;
              }
          }
      
          // Tables have same data
          return true;
      }
      
      private bool arraysHaveSameContent(object[] a, object[] b)
      {
          // Here your super cool method to compare the two arrays with LINQ,
          // or if you are a loser do it with a for loop :D
      }
      

      【讨论】:

      • @Davide Piras -Loser 的使用循环?如果您针对低于 .NET 3.5 的东西进行编程怎么办?真是个狠人。
      • 是的,这是个玩笑,我放了一个大 :D :D :D
      【解决方案10】:

      samneric 使用 DataRowComparer.Default 的回答的启发,但需要一些只能比较 DataTable 中的列子集的东西,我制作了一个 DataTableComparer 对象,您可以在其中指定要在比较中使用的列。如果他们有不同的列/架构,那就特别棒了。

      DataRowComparer.Default 之所以有效,是因为它实现了 IEqualityComparer。然后我创建了一个对象,您可以在其中定义要比较 DataRow 的哪些列。

      public class DataTableComparer : IEqualityComparer<DataRow>
      {
          private IEnumerable<String> g_TestColumns;
          public void SetCompareColumns(IEnumerable<String> p_Columns)
          {
              g_TestColumns = p_Columns; 
          }
      
          public bool Equals(DataRow x, DataRow y)
          {
      
              foreach (String sCol in g_TestColumns)
                  if (!x[sCol].Equals(y[sCol])) return false;
      
              return true;
          }
      
          public int GetHashCode(DataRow obj)
          {
              StringBuilder hashBuff = new StringBuilder();
      
              foreach (String sCol in g_TestColumns)
                  hashBuff.AppendLine(obj[sCol].ToString());               
      
              return hashBuff.ToString().GetHashCode();
      
          }
      }
      

      您可以通过以下方式使用它:

      DataTableComparer comp = new DataTableComparer();
      comp.SetCompareColumns(new String[] { "Name", "DoB" });
      
      DataTable celebrities = SomeDataTableSource();
      DataTable politicians = SomeDataTableSource2();
      
      List<DataRow> celebrityPoliticians = celebrities.AsEnumerable().Intersect(politicians.AsEnumerable(), comp).ToList();
      

      【讨论】:

        【解决方案11】:

        合并2个数据表然后比较变化如何?不确定这是否能满足您 100% 的需求,但为了快速比较,它会起作用。

        public DataTable GetTwoDataTablesChanges(DataTable firstDataTable, DataTable secondDataTable)
        { 
             firstDataTable.Merge(secondDataTable);
             return secondDataTable.GetChanges();
        }
        

        您可以阅读有关 DataTable.Merge() 的更多信息

        here

        【讨论】:

          【解决方案12】:

          如果您在数据库中有表,则可以进行完全外连接以获取差异。示例:

          select t1.Field1, t1.Field2, t2.Field1, t2.Field2
          from Table1 t1
          full outer join Table2 t2 on t1.Field1 = t2.Field1 and t1.Field2 = t2.Field2
          where t1.Field1 is null or t2.Field2 is null
          

          过滤掉所有相同的记录。前两个或后两个字段中有数据,具体取决于记录来自哪个表。

          【讨论】:

          • -我想在不涉及 SQL Server 的情况下进行比较(试图节省到 DB 的往返行程),所以在这种情况下这不适合我。
          • 这个问题是比较两个数据表。数据可能来自不同的数据库来源。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-14
          • 1970-01-01
          • 2020-02-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多