查找第二个数据表中某些列的匹配项

时间:2015-06-23 13:15:05

标签: c# linq datatable

我有两个DataTables dtChild dtMaster enter image description here

对于 dtChild 中的每一行,我想看看 dtMaster 中是否存在匹配项。此匹配基于特定列。

因此,在上图中,DataTables都有名为 Col2 Col3 的列。这些是我们感兴趣的列。在第二行,我们有匹配。因为 dtMaster 中有一行,其中 Col2 的值等于 Value22 ,而 Col3 的值等于< EM> Value23 。

我想编写一个LINQ查询,为第一行结果返回null(因为 dtChild 中的第一行没有匹配),第二行返回id dtMaster 中找到的记录(我们假设 dtMaster 在这种情况下也有一个名为Id的主键列)。

N.B。每次运行程序时,列名都会有所不同。所以我们希望我们的LINQ是动态的。此外,匹配列的数量(上例中的2)可以变化。因此,可能存在我们的条件基于5列的值的情况。

2 个答案:

答案 0 :(得分:1)

您可以使用:

DataTable dtResult = dtChild.Clone();
foreach(DataRow row in dtChild.Rows)
{
    DataRow newRow = dtResult.Rows.Add();
    newRow.SetField("Col1", row.Field<string>("Col1"));
    DataRow firstmatchingRow = dtMaster.AsEnumerable()
        .FirstOrDefault(r => r.Field<string>("Col2") == row.Field<string>("Col2")
                          && r.Field<string>("Col3") == row.Field<string>("Col3"));
    string col2 = null;
    string col3 = null;
    if(firstmatchingRow != null)
    {
        col2 = firstmatchingRow.Field<string>("Col2");
        col3 = firstmatchingRow.Field<string>("Col3");
    }
    newRow.SetField("Col2", col2);
    newRow.SetField("Col3", col3);
}

如果您想要一种动态方法,您可以在其中指定两个表的键列,您可以使用它:

string[] keyColumnNames = { "Col2", "Col3" };
DataTable dtResult = dtChild.Clone();

DataColumn[] childColumns = dtResult.Columns.Cast<DataColumn>() 
    .Where(c => keyColumnNames.Contains(c.ColumnName)) 
    .ToArray();
DataColumn[] masterColumns = dtMaster.Columns.Cast<DataColumn>() 
    .Where(c => keyColumnNames.Contains(c.ColumnName)) 
    .ToArray();

foreach (DataRow row in dtChild.Rows)
{
    DataRow newRow = dtResult.Rows.Add();
    newRow.SetField("Col1", row.Field<string>("Col1"));
    var matchingRows = dtMaster.AsEnumerable()
        .Where(masterRow => !masterColumns.Select(mc => masterRow.Field<string>(mc))
            .Except(childColumns.Select(cc => row.Field<string>(cc)))
            .Any());
    DataRow firstMatchingRow = matchingRows.FirstOrDefault();
    foreach(DataColumn col in childColumns)
        newRow.SetField(col, firstMatchingRow == null 
            ? null 
            : firstMatchingRow.Field<string>(col.ColumnName));
}

答案 1 :(得分:0)

未经测试......

class Data<T>()
{
  public int Id {get; set;}
  public T Value{get; set;}
}

var cols=dtChild.Columns.OfType<DataColumn>().All(c=>c.Name).toList();
var idCol=cols.Single(c=>c=="Id");
var valcols=cols.Where(c=>c!="Id");

var lst=new List<Data>();
var chlds=dtChild.Rows.OfType(DataRow);
chlds.ToList().ForEach(c=>lst.Add(new Data{ Id=c[idCol], Value=null} ); //initialize to null

foreach(var r1 in chlds)
{
  foreach(var r2 in dtMaster.Rows.OfType(DataRow))
  {
    if (r1[idcol]==r2[idCol])
    {
       forech(var c in valCols)
       {
        if (r1[c]==r2[c])
        {
          lst.Single(l=>l.Id==r1[c]).Value= r2[idCol];
          break;
        }
       }
    }
  }
}