2012-05-08 53 views
5

Estoy tratando de unir dos tablas de datos juntos de una manera similar a esta pregunta:C# DataTable combinación interna con columnas dinámicas

Inner join of DataTables in C#

que estoy tratando de conseguir que la salida sea un solo 'combinado 'tabla, con columnas de ambas tablas originales. Ambos tendrán una columna de fecha y hora en común.

La respuesta dada es buena para DataTables con columnas fijas, pero ¿qué ocurre si se crean dinámicamente y pueden tener cualquier cantidad de columnas? ¿Cómo puedo unirme a ellas?

p. Ej.

T1 (datestamp, t1Column1, t1Column2, t1ColumnN...) 
T2 (datestamp, t2Column1, t2Column2, t2ColumnN...) 

quisiera unirse para crear los siguientes:

J1 (datestamp, t1Column1, t1Column2, t1ColumnN, ..., t2Column1, t2Column2, t2ColumnN...) 

Es esto posible?

+0

¿Se puede usar "linq"? –

+0

¿Podría dar un ejemplo de algunos datos que serían el resultado que desea? –

+0

No creo que Linq sea una opción, ya que necesitaría saber los nombres de las columnas en las que intentaba proyectarse. – finoutlook

Respuesta

7

he encontrado una solución que no se base en bucle a través de las columnas.

Utiliza el método 'Merge', que anteriormente había descartado porque pensaba que ambas tablas requerían la misma estructura.

En primer lugar es necesario crear una clave principal en los dos datos en tablas:

// set primary key 
T1.PrimaryKey = new DataColumn[] { T1.Columns["DateStamp"] }; 
T2.PrimaryKey = new DataColumn[] { T2.Columns["DateStamp"] }; 

A continuación, agregue las dos tablas a un así una relación de conjunto de datos se pueden añadir:

// add both data-tables to data-set 
DataSet dsContainer = new DataSet(); 
dsContainer.Tables.Add(T1); 
dsContainer.Tables.Add(T2); 

Siguiente añadir la relación entre las dos columnas de clave en el conjunto de datos:

// add a relationship between the two timestamp columns 
DataRelation relDateStamp = new DataRelation("Date", new DataColumn[] { T1.Columns["DateStamp"] }, new DataColumn[] { T2.Columns["DateStamp"] }); 
dsContainer.Relations.Add(relDateStamp); 

Finalmente, puede ahora copiar el primer los datos de la tabla en una nueva versión de 'combinado', y luego se funden en el segundo:

// populate combined data 
DataTable dtCombined = new DataTable(); 
dtCombined = T1.Copy(); 
dtCombined.Merge(T2, false, MissingSchemaAction.Add); 

Nota: El método Merge requiere que el segundo argumento que es falsa o de lo contrario las copias de la estructura, pero no los datos de la segunda mesa.

Esto entonces combinar las siguientes tablas:

T1 (2012-05-09, 111, 222) 
T2 (2012-05-09, 333, 444, 555) 

en una versión combinada basada en la clave principal:

J1 (2012-05-09, 111, 222, 333, 444, 555) 
1

Creo que puede adaptar la respuesta en la pregunta vinculada para usar el index of the column, en lugar del nombre de la columna. O usted podría recorrer los elementos en cada fila, como este:

foreach(DataRow row in table.Rows) 
{ 
    foreach(DataColumn column in table.Columns) 
    { 
     object value = row[column]; // add this to your combined table 
    } 
} 
+0

Gracias - Esperaba evitar pasar por todas las columnas, ya que tengo algunos bucles anidados para configurar las tablas originales. Esto podría volverse bastante complejo si los datos en las tablas tienen muchas fechas/claves diferentes. – finoutlook

1

Después de cansarse de ver toda unirse a estos interno funciones que don' t emula SQL de manera confiable, decidí hacer mi propio aquí:

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; 
} 

Un examen de cómo puede usar esto:

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

Bien hecho. ¿Hay alguna forma de modificarlo para poder especificar un AND o un operador OR entre múltiples condiciones de "join on"? – Igor

Cuestiones relacionadas