繁体   English   中英

比较2个数据表以查找列之间的差异/准确性

[英]Compare 2 Datatables to find difference/accuracy between the columns

因此,我有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不会更快,至少在一般情况下不会更快。 但这可以帮助提高可读性。

您可以使用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);
    }
}
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);
        }
    }
}

使用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(DataRow)行。Field会将其强制转换为正确的类型

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

这使用类似于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。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM