2009-03-20 11 views
35

Niech T1 i T2 są DataTable s z następujących dziedzinWewnętrzna dołączyć z DataTables w C#

T1(CustID, ColX, ColY) 

T2(CustID, ColZ) 

muszę wspólną tabelę

TJ (CustID, ColX, ColY, ColZ) 

Jak można to zrobić w kod C# w prosty sposób ? Dzięki.

Odpowiedz

46

Jeśli możesz używać LINQ, spójrz na poniższy przykład. Tworzy dwa DataTables z kolumnami całkowitymi, wypełnia je niektórymi rekordami, łączy je za pomocą zapytania LINQ i wysyła je do konsoli.

DataTable dt1 = new DataTable(); 
    dt1.Columns.Add("CustID", typeof(int)); 
    dt1.Columns.Add("ColX", typeof(int)); 
    dt1.Columns.Add("ColY", typeof(int)); 

    DataTable dt2 = new DataTable(); 
    dt2.Columns.Add("CustID", typeof(int)); 
    dt2.Columns.Add("ColZ", typeof(int)); 

    for (int i = 1; i <= 5; i++) 
    { 
     DataRow row = dt1.NewRow(); 
     row["CustID"] = i; 
     row["ColX"] = 10 + i; 
     row["ColY"] = 20 + i; 
     dt1.Rows.Add(row); 

     row = dt2.NewRow(); 
     row["CustID"] = i; 
     row["ColZ"] = 30 + i; 
     dt2.Rows.Add(row); 
    } 

    var results = from table1 in dt1.AsEnumerable() 
       join table2 in dt2.AsEnumerable() on (int)table1["CustID"] equals (int)table2["CustID"] 
       select new 
       { 
        CustID = (int)table1["CustID"], 
        ColX = (int)table1["ColX"], 
        ColY = (int)table1["ColY"], 
        ColZ = (int)table2["ColZ"] 
       }; 
    foreach (var item in results) 
    { 
     Console.WriteLine(String.Format("ID = {0}, ColX = {1}, ColY = {2}, ColZ = {3}", item.CustID, item.ColX, item.ColY, item.ColZ)); 
    } 
    Console.ReadLine(); 

// Output: 
// ID = 1, ColX = 11, ColY = 21, ColZ = 31 
// ID = 2, ColX = 12, ColY = 22, ColZ = 32 
// ID = 3, ColX = 13, ColY = 23, ColZ = 33 
// ID = 4, ColX = 14, ColY = 24, ColZ = 34 
// ID = 5, ColX = 15, ColY = 25, ColZ = 35 
+4

To ostatecznie pracował dla mnie, ale spędziłem ponad godzinę na nim, z wyjątkiem „wyszczególnionego szarego nie ważne”, kwestionując moją poczytalność, dopóki nie zmienił go z (int) Tabela1 [ „pola”] do Convert.ToInt32 (table1 ["field"]). – CindyH

+1

@CindyH, wielkie dzięki za komentarz, zaoszczędziło mi to czasu. Przypuszczam, że dzieje się tak z powodu System.Data.DataTypes, gdzie int nie jest zawarty, ale Int32 jest. Lista wszystkich DataTypes tutaj: https://msdn.microsoft.com/en-us/library/system.data.datacolumn.datatype(v=vs.110).aspx – FrenkyB

27

Potrzebowałem funkcji, która łączyłaby tabele bez konieczności definiowania kolumn przy użyciu anonimowego selektora typów, ale miał trudności ze znalezieniem jakiegokolwiek. Skończyło się na tym, że musiałem stworzyć własną. Mam nadzieję, że to pomoże nikomu w przyszłości kto szuka to:

private DataTable JoinDataTables(DataTable t1, DataTable t2, params Func<DataRow, DataRow, bool>[] joinOn) 
{ 
    DataTable result = new DataTable(); 
    foreach (DataColumn col in t1.Columns) 
    { 
     if (result.Columns[col.ColumnName] == null) 
      result.Columns.Add(col.ColumnName, col.DataType); 
    } 
    foreach (DataColumn col in t2.Columns) 
    { 
     if (result.Columns[col.ColumnName] == null) 
      result.Columns.Add(col.ColumnName, col.DataType); 
    } 
    foreach (DataRow row1 in t1.Rows) 
    { 
     var joinRows = t2.AsEnumerable().Where(row2 => 
      { 
       foreach (var parameter in joinOn) 
       { 
        if (!parameter(row1, row2)) return false; 
       } 
       return true; 
      }); 
     foreach (DataRow fromRow in joinRows) 
     { 
      DataRow insertRow = result.NewRow(); 
      foreach (DataColumn col1 in t1.Columns) 
      { 
       insertRow[col1.ColumnName] = row1[col1.ColumnName]; 
      } 
      foreach (DataColumn col2 in t2.Columns) 
      { 
       insertRow[col2.ColumnName] = fromRow[col2.ColumnName]; 
      } 
      result.Rows.Add(insertRow); 
     } 
    } 
    return result; 
} 

Przykładem, jak można to wykorzystać:

var test = JoinDataTables(transactionInfo, transactionItems, 
       (row1, row2) => 
       row1.Field<int>("TransactionID") == row2.Field<int>("TransactionID")); 

Jedno zastrzeżenie: To z pewnością nie jest zoptymalizowany, więc być świadomy podczas pobierania liczba wierszy powyżej 20k. Jeśli wiesz, że jeden stół będzie większy od drugiego, spróbuj umieścić mniejszą pierwszą i większą jedną sekundę.

+0

Czy istnieje sposób, aby go zmodyfikować, aby móc określić operatora AND lub OR między wieloma warunkami "dołączenia"? – Igor

+0

Najprostszym sposobem jest niezmienianie kodu i spakowanie wszystkich warunków do jednego połączenia pod warunkiem. Na przykład: 'row1.Field (" Id ") == row2.Field (" Id ") || wiersz1.Field ("CustId") == wiersz2.Field ("CustId") ' – Bognar

+0

Ale wtedy nie można byłoby określić dynamicznie operatora logicznego. – Igor

5

To jest mój kod. Nie idealny, ale działa dobrze. Mam nadzieję, że to pomoże ktoś:

static System.Data.DataTable DtTbl (System.Data.DataTable[] dtToJoin) 
    { 
     System.Data.DataTable dtJoined = new System.Data.DataTable(); 

     foreach (System.Data.DataColumn dc in dtToJoin[0].Columns) 
      dtJoined.Columns.Add(dc.ColumnName); 

     foreach (System.Data.DataTable dt in dtToJoin) 
      foreach (System.Data.DataRow dr1 in dt.Rows) 
      { 
       System.Data.DataRow dr = dtJoined.NewRow(); 
       foreach (System.Data.DataColumn dc in dtToJoin[0].Columns) 
        dr[dc.ColumnName] = dr1[dc.ColumnName]; 

       dtJoined.Rows.Add(dr); 
      } 

     return dtJoined; 
    } 
1

funkcja ta dołączy do 2 tabele ze znanym dołączyć boiska, ale to nie może pozwolić 2 pola o tej samej nazwie w obu tabelach z wyjątkiem pola połączeniowego, prosta modyfikacja byłoby zaoszczędzić słownik z licznikiem i po prostu dodaj numer do tej samej nazwy filds.

public static DataTable JoinDataTable(DataTable dataTable1, DataTable dataTable2, string joinField) 
{ 
    var dt = new DataTable(); 
    var joinTable = from t1 in dataTable1.AsEnumerable() 
          join t2 in dataTable2.AsEnumerable() 
           on t1[joinField] equals t2[joinField] 
          select new { t1, t2 }; 

    foreach (DataColumn col in dataTable1.Columns) 
     dt.Columns.Add(col.ColumnName, typeof(string)); 

    dt.Columns.Remove(joinField); 

    foreach (DataColumn col in dataTable2.Columns) 
     dt.Columns.Add(col.ColumnName, typeof(string)); 

    foreach (var row in joinTable) 
    { 
     var newRow = dt.NewRow(); 
     newRow.ItemArray = row.t1.ItemArray.Union(row.t2.ItemArray).ToArray(); 
     dt.Rows.Add(newRow); 
    } 
    return dt; 
}