【问题标题】:Compare 2 Datatables to find difference/accuracy between the columns比较 2 个数据表以查找列之间的差异/准确性
【发布时间】:2014-03-26 15:42:30
【问题描述】:

所以,我有 2 个单独的数据表,它们看起来非常相似,但例如它们的行中的值可能不同。

编辑:

我可以通过创建一个可以用作主键的临时标识列来获得一个唯一的 ID,如果这样会更容易的话。所以将 ID 列视为主键。

表 A

ID |  Name | Value1 | Value2 | Value3
-------------------------------------
1  |  Bob  |   50   |  150   |  35
2  |  Bill |   55   |  47    |  98
3  |  Pat  |   10   |  15    |  45
4  |  Cat  |   70   |  150   |  35

表 B

ID |  Name | Value1 | Value2 | Value3
-------------------------------------
1  |  Bob  |   30   |  34    |  67
2  |  Bill |   55   |  47    |  98
3  |  Pat  |   100  |  15    |  45
4  |  Cat  |   70   |  100   |  20

输出应该是:

表 C

ID |  Name | TableAValue1 | TableBValue1 | DiffValue1 ....Samething for Value2 .....samething for value3
------------------------------------------------------
1  |  Bob  |   50         |   30         |    20          
2  |  Bill |   55         |   55         |    0               
3  |  Pat  |   10         |   100        |    90                
4  |  Cat  |   70         |   70         |    0                    

我知道执行此操作的繁琐方法是使用 forloop 并循环遍历每一行,并将列行相互比较。但我不确定如何使用我想要的结果创建一个新的表 C。此外,我认为使用Linq 可能有一个更简单的解决方案,我不太熟悉,但如果它更快且代码行更少,我会对linq 的解决方案感兴趣。 我正在寻找解决此问题的最优化/最有效的方法。因为这些数据表的大小可以在 5,000 到 15,000+ 行之间,因此内存使用成为问题。

【问题讨论】:

  • LINQ 在内部使用循环,因为您的 ID 不是唯一的,您需要逐行比较。我不确定 LINQ 是否会提高性能。
  • 如何匹配两个表之间的行?它们是按名称键入的吗?还是通过组合或 ID 和名称?或者我们可以假设每个表的行数相同,并且行的顺序在它们之间完全匹配?
  • @StriplingWarrior 是的,每个表将具有相同数量的行/列,并且两个表的列名也将相同。但是表之间的行中没有唯一的主键..
  • @StriplingWarrior 我已经编辑了我的帖子,请查看更新后的问题,忽略我之前的评论。
  • @PrakashChennupati:没关系。我会建议使用.Zip() LINQ 函数将两者结合起来,这比在 ID 上进行连接要快一些。另一个问题:您的输出是否必须采用 DataTable 的形式?由于内存和性能是一个问题,您最好使用代表输出中列的对象列表,而不是 DataTable。但我不知道你以后打算如何使用这些数据。

标签: c# asp.net linq datatable


【解决方案1】:

LINQ 并不快,至少在一般情况下不会。但它可以帮助提高可读性。

您可以使用Enumerable.Join,它可能比嵌套循环更有效,但无论如何您都需要一个循环来填充第三个表。所以前两列是标识符,其余的是值:

var query = from r1 in table1.AsEnumerable()
            join r2 in table2.AsEnumerable()
            on new { ID = r1.Field<int>("ID"), Name = r1.Field<string>("Name") }
            equals new { ID = r2.Field<int>("ID"), Name = r2.Field<string>("Name") }
            select new { r1, r2 };

var columnsToCompare = table1.Columns.Cast<DataColumn>().Skip(2);

foreach (var rowInfo in query)
{
    var row = table3.Rows.Add();
    row.SetField("ID", rowInfo.r1.Field<int>("ID"));
    row.SetField("Name", rowInfo.r1.Field<int>("Name"));
    foreach (DataColumn col in columnsToCompare)
    { 
        int val1 = rowInfo.r1.Field<int>(col.ColumnName);
        int val2 = rowInfo.r2.Field<int>(col.ColumnName);
        int diff = (int)Math.Abs(val1-val2);
        row.SetField(col.ColumnName, diff);
    }
}

【讨论】:

    【解决方案2】:
    var tableC = new DataTable();
    tableC.Columns.Add(new DataColumn("ID"));
    tableC.Columns.Add(new DataColumn("Name"));
    tableC.Columns.Add(new DataColumn("TableAValue1"));
    tableC.Columns.Add(new DataColumn("TableBValue1"));
    tableC.Columns.Add(new DataColumn("DiffValue1"));
    foreach (DataRow rowA in tableA.Rows)
    {
        foreach (DataRow rowB in tableB.Rows)
        {
            if (Convert.ToInt32(rowA["ID"]) == Convert.ToInt32(rowB["ID"]) &&
                rowA["Name"].ToString() == rowB["Name"].ToString() &&
                Convert.ToInt32(rowA["Value1"]) != Convert.ToInt32(rowB["Value1"]))
            {
                var newRow = tableC.NewRow();
                newRow["ID"] = rowA["ID"];
                newRow["Name"] = rowA["Name"];
                newRow["TableAValue1"] = rowA["Value1"];
                newRow["TableBValue1"] = rowB["Value1"];
                newRow["DiffValue1"] = Convert.ToInt32(rowA["Value1"]) - Convert.ToInt32(rowB["Value1"]);
                tableC.Rows.Add(newRow);
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      使用LINQ,创建一个匿名类型如下

          var joinedRows = (from rowA in TableA.AsEnumerable()
                            from rowB in TableB.AsEnumerable()
                            where rowA.Field<String>("Name") == rowB.Field<String>("Name")
                            select new
                                       {
                                           ID = rowA.Field<int>("ID"),
                                           Name = rowA.Field<String>("Name"),
                                           TableAValue1 = rowA.Field<int>("Value1"),
                                           TableBValue1 = rowB.Field<int>("Value1"),
                                           DiffValue1 = Math.Abs(rowA.Field<int>("Value1") - rowB.Field<int>("Value1")),
                                           TableAValue2 = rowA.Field<int>("Value2"),
                                           TableBValue2 = rowB.Field<int>("Value2"),
                                           DiffValue2 = Math.Abs(rowA.Field<int>("Value2") - rowB.Field<int>("Value2")),
                                           TableAValue3 = rowA.Field<int>("Value3"),
                                           TableBValue3 = rowB.Field<int>("Value3"),
                                           DiffValue3 = Math.Abs(rowA.Field<int>("Value3") - rowB.Field<int>("Value3"))
                                       });
      

      Table.AsEnumerable() 会给你一个 IEnumerable(of DataRow) row.Field 将为您将其转换为正确的类型

      您现在可以使用匿名类型的joinedRows 并从中创建新的dataTable

      【讨论】:

        【解决方案4】:

        这使用了类似于 kippermand 的策略,但通过避免检查每个 ID 与其他 ID 的 O(n²) 复杂性以及重用从数据表中提取的值,可能会在大型数据集上表现稍好:

        // joining by row location
        var joinedTableRows =
            dt1.AsEnumerable().Zip(dt2.AsEnumerable(),
                (r1, r2) => new{r1, r2});
        // or, joining by ID
        var joinedTableRows2 =
            dt1.AsEnumerable().Join(dt2.AsEnumerable(),
                r => r.Field<int>("ID"),
                r => r.Field<int>("ID"),
                (r1, r2) => new{r1, r2});
        
        var result =
            from row in joinedTableRows
            let rowA = row.r1
            let rowB = row.r2
            let tableAValue1 = rowA.Field<int>("Value1")
            let tableBValue1 = rowB.Field<int>("Value1")
            let tableAValue2 = rowA.Field<int>("Value2")
            let tableBValue2 = rowB.Field<int>("Value2")
            let tableAValue3 = rowA.Field<int>("Value3")
            let tableBValue3 = rowB.Field<int>("Value3")
            select new
            {
                ID = row.r1.Field<int>("ID"),
                Name = row.r1.Field<string>("Name"),
                TableAValue1 = tableAValue1,
                TableBValue1 = tableBValue1,
                DiffValue1 = Math.Abs(tableAValue1 - tableBValue1),
                TableAValue2 = tableAValue2,
                TableBValue2 = tableBValue2,
                DiffValue2 = Math.Abs(tableAValue2 - tableBValue2),
                TableAValue3 = tableAValue3,
                TableBValue3 = tableBValue3,
                DiffValue3 = Math.Abs(tableAValue3 - tableBValue3)
            };
        

        根据需要如何使用数据,您可以声明一个匹配此匿名类型的类,然后直接使用它(这是我更喜欢的),或者您可以从这些对象创建一个 DataTable,如果您必须。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-02-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-08-05
          • 2023-03-27
          相关资源
          最近更新 更多