2013-05-29 41 views
8

我有2个数据表,我不知道它们的数据列表。该列表必须在运行时提取,并用于完整外连接。在2个数据表上使用列表的全外连接

使用这些列时,两个表之间的列需要合并,我需要显示所有数据。

直到现在我在做什么是

  1. 获取公共列,使用相交,并执行的IEqualityComparer
  2. 创建一个新的数据表,这些列,从而使2个数据表将被合并到这个新表

但是,我在第二步中遇到了Linq问题。

到目前为止我有:

获取公共列

 

    // Get common columns 
    var commonColumns = dt1.Columns.OfType().Intersect(dt2.Columns.OfType(), new DataColumnComparer()); 

创建新的数据表

 

    // Create the result which is going to be sent to the user 
    DataTable result = new DataTable(); 

    // Add all the columns from both tables 
    result.Columns.AddRange(
    dt1.Columns.OfType() 
    .Union(dt2.Columns.OfType(), new DataColumnComparer()) 
    .Select(c => new DataColumn(c.Caption, c.DataType, c.Expression, c.ColumnMapping)).ToArray()); 

我怎样才能获得有效的全外动态加入,从在运行时提取的datacolumns列表?

回答

4

var commonColumns = dt1.Columns.OfType<DataColumn>().Intersect(dt2.Columns.OfType<DataColumn>(), new DataColumnComparer()); 
     DataTable result = new DataTable(); 

     dt1.PrimaryKey = commonColumns.ToArray(); 

     result.Merge(dt1, false, MissingSchemaAction.AddWithKey); 
     result.Merge(dt2, false, MissingSchemaAction.AddWithKey); 
+0

像魅力一样工作 - 欢呼马特! – Mez

0

我挣扎也得到答案,我正在复制粘贴整个代码。我相信这会帮助你。

你只需要DataTable1DataTable2primarykeys这两个表上将进行此连接。您可以设置数据表的主键

datatable1.PrimaryKey = new DataColumn[] { captureDT.Columns["Your Key Name"] }; 

//你的代码

/// <summary> 
    /// Combines the data of two data table into a single data table. The grouping of tables 
    /// will be based on the primary key provided for both the tables. 
    /// </summary> 
    /// <param name="table1"></param> 
    /// <param name="table2"></param> 
    /// <param name="table1PrimaryKey"></param> 
    /// <param name="table2PrimaryKey"></param> 
    /// <returns></returns> 
    private DataTable DataTablesOuterJoin(DataTable table1, DataTable table2, string table1PrimaryKey, string table2PrimaryKey) 
    { 
     DataTable flatDataTable = new DataTable(); 

     foreach (DataColumn column in table2.Columns) 
     { 
      flatDataTable.Columns.Add(new DataColumn(column.ToString())); 
     } 
     foreach (DataColumn column in table1.Columns) 
     { 
      flatDataTable.Columns.Add(new DataColumn(column.ToString())); 
     } 

     // Retrun empty table with required columns to generate empty extract 
     if (table1.Rows.Count <= 0 && table2.Rows.Count <= 0) 
     { 
      flatDataTable.Columns.Remove(table2PrimaryKey); 
      return flatDataTable; 
     } 

     var dataBaseTable2 = table2.AsEnumerable(); 
     var groupDataT2toT1 = dataBaseTable2.GroupJoin(table1.AsEnumerable(), 
           br => new { id = br.Field<string>(table2PrimaryKey).Trim().ToLower() }, 
           jr => new { id = jr.Field<string>(table1PrimaryKey).Trim().ToLower() }, 
           (baseRow, joinRow) => joinRow.DefaultIfEmpty() 
            .Select(row => new 
            { 
             flatRow = baseRow.ItemArray.Concat((row == null) ? new object[table1.Columns.Count] : 
             row.ItemArray).ToArray() 
            })).SelectMany(s => s); 

     var dataBaseTable1 = table1.AsEnumerable(); 
     var groupDataT1toT2 = dataBaseTable1.GroupJoin(table2.Select(), 
           br => new { id = br.Field<string>(table1PrimaryKey).Trim().ToLower() }, 
           jr => new { id = jr.Field<string>(table2PrimaryKey).Trim().ToLower() }, 
           (baseRow, joinRow) => joinRow.DefaultIfEmpty() 
            .Select(row => new 
            { 
             flatRow = (row == null) ? new object[table2.Columns.Count].ToArray().Concat(baseRow.ItemArray).ToArray() : 
             row.ItemArray.Concat(baseRow.ItemArray).ToArray() 
            })).SelectMany(s => s); 

     // Get the union of both group data to single set 
     groupDataT2toT1 = groupDataT2toT1.Union(groupDataT1toT2); 

     // Load the grouped data to newly created table 
     foreach (var result in groupDataT2toT1) 
     { 
      flatDataTable.LoadDataRow(result.flatRow, false); 
     } 

     // Get the distinct rows only 
     IEnumerable rows = flatDataTable.Select().Distinct(DataRowComparer.Default); 

     // Create a new distinct table with same structure as flatDataTable 
     DataTable distinctFlatDataTable = flatDataTable.Clone(); 
     distinctFlatDataTable.Rows.Clear(); 

     // Push all the rows into distinct table. 
     // Note: There will be two different columns for primary key1 and primary key2. In grouped rows, 
     // primary key1 or primary key2 can have empty values. So copy all the primary key2 values to 
     // primary key1 only if primary key1 value is empty and then delete the primary key2. So at last 
     // we will get only one perimary key. Please make sure the non-deleted key must be present in 
     foreach (DataRow row in rows) 
     { 
      if (string.IsNullOrEmpty(row[table1PrimaryKey].ToString())) 
       row[table1PrimaryKey] = row[table2PrimaryKey]; 

      if (string.IsNullOrEmpty(row[CaptureBusDateColumn].ToString())) 
       row[CaptureBusDateColumn] = _businessDate; 

      if (string.IsNullOrEmpty(row[CaptureUserIDColumn].ToString())) 
       row[CaptureUserIDColumn] = row[StatsUserIDColumn]; 

      distinctFlatDataTable.ImportRow(row); 
     } 

     // Sort the table based on primary key. 
     DataTable sortedFinaltable = (from orderRow in distinctFlatDataTable.AsEnumerable() 
             orderby orderRow.Field<string>(table1PrimaryKey) 
             select orderRow).CopyToDataTable(); 

     // Remove primary key2 as we have already copied it to primary key1 
     sortedFinaltable.Columns.Remove(table2PrimaryKey); 

     return ReplaceNulls(sortedFinaltable, "0"); 
    } 

    /// <summary> 
    /// Replace all the null values from data table with specified string 
    /// </summary> 
    /// <param name="dt"></param> 
    /// <param name="replaceStr"></param> 
    /// <returns></returns> 
    private DataTable ReplaceNulls(DataTable dt, string replaceStr) 
    { 
     for (int a = 0; a < dt.Rows.Count; a++) 
     { 
      for (int i = 0; i < dt.Columns.Count; i++) 
      { 
       if (dt.Rows[a][i] == DBNull.Value) 
       { 
        dt.Rows[a][i] = replaceStr; 
       } 
      } 
     } 
     return dt; 
    } 
+0

不幸的是,这并不在我的案件,不适用,因为公共列的列表是在运行中提取时间,我可以列出8个公共列,我需要根据这些列创建连接。 – Mez

+0

有一个名为'flatDataTable'的新表,它具有从'datatable1'和'datatable2'动态提取的列... – PawanS

2

基于马修的回答,我创建了一个功能,这可能是工作接受超过2个数据表。我希望它能帮助:

用法:

var table123 = FullOuterJoinDataTables(table1, table2, table3); 

下面是函数来源:

public DataTable FullOuterJoinDataTables(params DataTable[] datatables) // supports as many datatables as you need. 
{ 
    DataTable result = datatables.First().Clone(); 

    var commonColumns = result.Columns.OfType<DataColumn>(); 

    foreach (var dt in datatables.Skip(1)) 
    { 
     commonColumns = commonColumns.Intersect(dt.Columns.OfType<DataColumn>(), new DataColumnComparer()); 
    } 

    result.PrimaryKey = commonColumns.ToArray(); 

    foreach (var dt in datatables) 
    { 
     result.Merge(dt, false, MissingSchemaAction.AddWithKey); 
    } 

    return result; 
} 

/* also create this class */ 
public class DataColumnComparer : IEqualityComparer<DataColumn> 
{ 
    public bool Equals(DataColumn x, DataColumn y) => x.Caption == y.Caption;  

    public int GetHashCode(DataColumn obj) => obj.Caption.GetHashCode();   
}