2013-07-16 84 views
14

如何在保留兩個表中的所有列的同時,使用以下表格和條件將兩個數據表格留在外部連接(我認爲它是左外連接但我不是100%確定)?如何左外連接在c#中的兩個數據表?

dtblLeft:

id col1 anotherColumn2 
1 1  any2 
2 1  any2 
3 2  any2 
4 3  any2 
5 3  any2 
6 3  any2 
7   any2 

dtblRight:

col1 col2  anotherColumn1 
1  Hi  any1 
2  Bye  any1 
3  Later  any1 
4  Never  any1 

dtblJoined:

id col1 col2  anotherColumn1  anotherColumn2 
1 1  Hi  any1    any2 
2 1  Hi  any1    any2 
3 2  Bye  any1    any2 
4 3  Later any1    any2 
5 3  Later any1    any2 
6 3  Later any1    any2 
7          any2 

條件:

  • 在dtblLeft中,col1不需要具有唯一值。
  • 在dtblRight中,col1具有唯一值。
  • 如果dtblLeft在col1中缺少一個外鍵,或者它有一個在dtblRight中不存在的外鍵,那麼將插入空字段或空字段。
  • 加入col1。

我可以使用普通的DataTable操作,LINQ,或什麼的。

我試過,但它消除重複:

dtblA.PrimaryKey = new DataColumn[] {dtblA.Columns["col1"]} 

DataTable dtblJoined = new DataTable(); 
dtblJoined.Merge(dtblA, false, MissingSchemaAction.AddWithKey); 
dtblJoined.Merge(dtblB, false, MissingSchemaAction.AddWithKey); 

編輯1:

這是靠近我我想要什麼,但它只有從表中的一個(列發現於此link):

dtblJoined = (from t1 in dtblA.Rows.Cast<DataRow>() 
        join t2 in dtblB.Rows.Cast<DataRow>() on t1["col1"] equals t2["col1"] 
        select t1).CopyToDataTable(); 

編輯2:

從這個link的回答似乎爲我工作,但我不得不改變這一點,如下所示:

DataTable targetTable = dtblA.Clone(); 
var dt2Columns = dtblB.Columns.OfType<DataColumn>().Select(dc => 
new DataColumn(dc.ColumnName, dc.DataType, dc.Expression, dc.ColumnMapping)); 
var dt2FinalColumns = from dc in dt2Columns.AsEnumerable() 
        where targetTable.Columns.Contains(dc.ColumnName) == false 
        select dc; 

targetTable.Columns.AddRange(dt2FinalColumns.ToArray()); 

var rowData = from row1 in dtblA.AsEnumerable() 
          join row2 in dtblB.AsEnumerable() 
          on row1["col1"] equals row2["col1"] 
          select row1.ItemArray.Concat(row2.ItemArray.Where(r2 => row1.ItemArray.Contains(r2) == false)).ToArray(); 

foreach (object[] values in rowData) 
     targetTable.Rows.Add(values); 

我也發現了這個link,我可能會嘗試了這一點,因爲它看起來更簡潔。

EDIT 3(2013年11月18日):

更新的表,以反映更多的情況。

+0

這也是自然連接。告訴我們dtblA有'4'和dtblB不會發生什麼。 – Shoe

+0

已更新,以顯示dtblA與4 – Soenhay

+0

以防萬一你錯過了關於同一問題上的這些其他職位 [請點擊這裏](http://stackoverflow.com/questions/10404039/left-join-datatables-how-do-我得到這個工作)[和這裏](http://stackoverflow.com/questions/9055180/datatables-left-join-c-sharp) – Rwiti

回答

10

感謝所有您的幫助。以下是我想出了基於多資源:

public static class DataTableHelper 
{ 
    public enum JoinType 
    { 
     /// <summary> 
     /// Same as regular join. Inner join produces only the set of records that match in both Table A and Table B. 
     /// </summary> 
     Inner = 0, 
     /// <summary> 
     /// Same as Left Outer join. Left outer join produces a complete set of records from Table A, with the matching records (where available) in Table B. If there is no match, the right side will contain null. 
     /// </summary> 
     Left = 1 
    } 

    /// <summary> 
    /// Joins the passed in DataTables on the colToJoinOn. 
    /// <para>Returns an appropriate DataTable with zero rows if the colToJoinOn does not exist in both tables.</para> 
    /// </summary> 
    /// <param name="dtblLeft"></param> 
    /// <param name="dtblRight"></param> 
    /// <param name="colToJoinOn"></param> 
    /// <param name="joinType"></param> 
    /// <returns></returns> 
    /// <remarks> 
    /// <para>http://stackoverflow.com/questions/2379747/create-combined-datatable-from-two-datatables-joined-with-linq-c-sharp?rq=1</para> 
    /// <para>http://msdn.microsoft.com/en-us/library/vstudio/bb397895.aspx</para> 
    /// <para>http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html</para> 
    /// <para>http://stackoverflow.com/questions/406294/left-join-and-left-outer-join-in-sql-server</para> 
    /// </remarks> 
    public static DataTable JoinTwoDataTablesOnOneColumn(DataTable dtblLeft, DataTable dtblRight, string colToJoinOn, JoinType joinType) 
    { 
     //Change column name to a temp name so the LINQ for getting row data will work properly. 
     string strTempColName = colToJoinOn + "_2"; 
     if (dtblRight.Columns.Contains(colToJoinOn)) 
      dtblRight.Columns[colToJoinOn].ColumnName = strTempColName; 

     //Get columns from dtblLeft 
     DataTable dtblResult = dtblLeft.Clone(); 

     //Get columns from dtblRight 
     var dt2Columns = dtblRight.Columns.OfType<DataColumn>().Select(dc => new DataColumn(dc.ColumnName, dc.DataType, dc.Expression, dc.ColumnMapping)); 

     //Get columns from dtblRight that are not in dtblLeft 
     var dt2FinalColumns = from dc in dt2Columns.AsEnumerable() 
           where !dtblResult.Columns.Contains(dc.ColumnName) 
           select dc; 

     //Add the rest of the columns to dtblResult 
     dtblResult.Columns.AddRange(dt2FinalColumns.ToArray()); 

     //No reason to continue if the colToJoinOn does not exist in both DataTables. 
     if (!dtblLeft.Columns.Contains(colToJoinOn) || (!dtblRight.Columns.Contains(colToJoinOn) && !dtblRight.Columns.Contains(strTempColName))) 
     { 
      if (!dtblResult.Columns.Contains(colToJoinOn)) 
       dtblResult.Columns.Add(colToJoinOn); 
      return dtblResult; 
     } 

     switch (joinType) 
     { 

      default: 
      case JoinType.Inner: 
       #region Inner 
       //get row data 
       //To use the DataTable.AsEnumerable() extension method you need to add a reference to the System.Data.DataSetExtension assembly in your project. 
       var rowDataLeftInner = from rowLeft in dtblLeft.AsEnumerable() 
             join rowRight in dtblRight.AsEnumerable() on rowLeft[colToJoinOn] equals rowRight[strTempColName] 
             select rowLeft.ItemArray.Concat(rowRight.ItemArray).ToArray(); 


       //Add row data to dtblResult 
       foreach (object[] values in rowDataLeftInner) 
        dtblResult.Rows.Add(values); 

       #endregion 
       break; 
      case JoinType.Left: 
       #region Left 
       var rowDataLeftOuter = from rowLeft in dtblLeft.AsEnumerable() 
             join rowRight in dtblRight.AsEnumerable() on rowLeft[colToJoinOn] equals rowRight[strTempColName] into gj 
             from subRight in gj.DefaultIfEmpty() 
             select rowLeft.ItemArray.Concat((subRight== null) ? (dtblRight.NewRow().ItemArray) :subRight.ItemArray).ToArray(); 


       //Add row data to dtblResult 
       foreach (object[] values in rowDataLeftOuter) 
        dtblResult.Rows.Add(values); 

       #endregion 
       break; 
     } 

     //Change column name back to original 
     dtblRight.Columns[strTempColName].ColumnName = colToJoinOn; 

     //Remove extra column from result 
     dtblResult.Columns.Remove(strTempColName); 

     return dtblResult; 
    } 
} 

編輯3:

這種方法現在可以正常工作,它仍然是快,當表有2000+行。任何建議/建議/改進將不勝感激。

編輯4:

我有一定的場景,使我認識到以前的版本是真的做一個內部聯接。該功能已被修改以解決該問題。我用這個link的信息弄清楚了。

0

你也許可以使用LINQ和做這樣的事情:

var dtblJoined = from dB in dtblB.AsEnumerable() 
       join dA in dtblA.AsEnumerable() on dA.col1 equals dB.col1 into dAB 
       from d in dAB.DefaultIfEmpty() 
       select new (col1 = dB.col1, ; col2 = (dB.col1 == dA.col1) ? dA.col2 : null); 

這將返回一個IEnumerable的結果不是一個DataTable,但它應該讓你更接近你在找什麼,我認爲。可能需要稍微調整一下。

1

這只是一個內部的2個表之間的連接:

var query = (from x in a.AsEnumerable() 
       join y in b.AsEnumerable() on x.Field<int>("col1") equals y.Field<int>("col1") 
       select new { col1= y.Field<int>("col1"), col2=x.Field<int>("col2") }).ToList(); 

產地:

col1 col2 
1 Hi 
1 Hi 
2 Bye 
3 Later 
3 Later 
3 Later