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

时间:2014-03-26 15:42:30

标签: c# asp.net linq datatable

所以,我有两个独立的数据表,看起来非常相同,但它们的行中的值可能会有所不同。

修改

我可以拥有一个唯一的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多行之间,因此内存使用成为一个问题。

4 个答案:

答案 0 :(得分:2)

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);
    }
}

答案 1 :(得分:1)

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);
        }
    }
}

答案 2 :(得分:1)

使用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) row.Field会将它转换为正确的类型

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

答案 3 :(得分:1)

这使用类似于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,如果你必须的话。