2013-05-29 86 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();   
}