Combinazione di n DataTable in un singolo DataTable

Tutto, ci sono alcune domande su questo, ma non riesco a estrarre abbastanza informazioni per risolvere il problema per il mio caso. Estraggo un numero sconosciuto di tabelle in SQL Server ‘Tab1’, ‘Tab2’, ‘Tab3’, …, ‘TabN’. Le colonne in queste tabelle sono diverse, ma le definizioni delle righe sono le stesse. Ho bisogno di estrarre tutti i dati dal server in N DataTable e combinarli per formare un singolo DataTable . Quello che faccio attualmente è

 int nTmpVolTabIdx = 1; strSqlTmp = String.Empty; using (DataTable dataTableALL = new DataTable()) { while (true) { string strTmpVolName = String.Format("Tab{0}", nTmpVolTabIdx); strSqlTmp = String.Format("SELECT * FROM [{0}];", strTmpVolName); // Pull the data from 'VolX' into a local DataTable. using (DataTable dataTable = UtilsDB.DTFromDB(conn, strTmpVolName, strSqlTmp, false)) { if (dataTable == null) break; else dataTableALL.Merge(dataTable); } nTmpVolTabIdx++; } ... } 

Questo unisce i DataTable ma sono allineati alla mancanza (riempimento di celle vuote sul set di dati aggiunto). Potrei aggiungere le colonne del nuovo DataTable tramite un ciclo; ma c’è un modo più semplice / bello per farlo (forse usando LINQ)?

Grazie per il tuo tempo.

Modificare. Per fornire i set di dati di esempio.

Quello che ho richiesto è

DataTable completo

I singoli tavoli sono

Tabs

Dopo la prima operazione di unione, ho il seguente

Dopo l'unione

Grazie ancora.

La tabella ha ripetuto le chiavi primarie dopo l’ Merge perché non è stata definita alcuna chiave primaria. Quindi specificate il PK o provate questo metodo qui che ho scritto da zero (quindi non è realmente testato) :

 public static DataTable MergeAll(this IList tables, String primaryKeyColumn) { if (!tables.Any()) throw new ArgumentException("Tables must not be empty", "tables"); if(primaryKeyColumn != null) foreach(DataTable t in tables) if(!t.Columns.Contains(primaryKeyColumn)) throw new ArgumentException("All tables must have the specified primarykey column " + primaryKeyColumn, "primaryKeyColumn"); if(tables.Count == 1) return tables[0]; DataTable table = new DataTable("TblUnion"); table.BeginLoadData(); // Turns off notifications, index maintenance, and constraints while loading data foreach (DataTable t in tables) { table.Merge(t); // same as table.Merge(t, false, MissingSchemaAction.Add); } table.EndLoadData(); if (primaryKeyColumn != null) { // since we might have no real primary keys defined, the rows now might have repeating fields // so now we're going to "join" these rows ... var pkGroups = table.AsEnumerable() .GroupBy(r => r[primaryKeyColumn]); var dupGroups = pkGroups.Where(g => g.Count() > 1); foreach (var grpDup in dupGroups) { // use first row and modify it DataRow firstRow = grpDup.First(); foreach (DataColumn c in table.Columns) { if (firstRow.IsNull(c)) { DataRow firstNotNullRow = grpDup.Skip(1).FirstOrDefault(r => !r.IsNull(c)); if (firstNotNullRow != null) firstRow[c] = firstNotNullRow[c]; } } // remove all but first row var rowsToRemove = grpDup.Skip(1); foreach(DataRow rowToRemove in rowsToRemove) table.Rows.Remove(rowToRemove); } } return table; } 

Puoi chiamarlo in questo modo:

 var tables = new[] { tblA, tblB, tblC }; DataTable TblUnion = tables.MergeAll("c1"); 

Utilizzato questo esempio di dati:

 var tblA = new DataTable(); tblA.Columns.Add("c1", typeof(int)); tblA.Columns.Add("c2", typeof(int)); tblA.Columns.Add("c3", typeof(string)); tblA.Columns.Add("c4", typeof(char)); var tblB = new DataTable(); tblB.Columns.Add("c1", typeof(int)); tblB.Columns.Add("c5", typeof(int)); tblB.Columns.Add("c6", typeof(string)); tblB.Columns.Add("c7", typeof(char)); var tblC = new DataTable(); tblC.Columns.Add("c1", typeof(int)); tblC.Columns.Add("c8", typeof(int)); tblC.Columns.Add("c9", typeof(string)); tblC.Columns.Add("c10", typeof(char)); tblA.Rows.Add(1, 8500, "abc", 'A'); tblA.Rows.Add(2, 950, "cde", 'B'); tblA.Rows.Add(3, 150, "efg", 'C'); tblA.Rows.Add(4, 850, "ghi", 'D'); tblA.Rows.Add(5, 50, "ijk", 'E'); tblB.Rows.Add(1, 7500, "klm", 'F'); tblB.Rows.Add(2, 900, "mno", 'G'); tblB.Rows.Add(3, 150, "opq", 'H'); tblB.Rows.Add(4, 850, "qrs", 'I'); tblB.Rows.Add(5, 50, "stu", 'J'); tblC.Rows.Add(1, 7500, "uvw", 'K'); tblC.Rows.Add(2, 900, "wxy", 'L'); tblC.Rows.Add(3, 150, "yza", 'M'); tblC.Rows.Add(4, 850, "ABC", 'N'); tblC.Rows.Add(5, 50, "CDE", 'O'); 

Dopo DataTable.Merge in MergeAll :

inserisci la descrizione dell'immagine qui

Dopo alcune modifiche per unire le righe in MergeAll :

inserisci la descrizione dell'immagine qui


Aggiornare

Poiché questa domanda è sorta in uno dei commenti, se l’unica relazione tra due tabelle è l’indice di una DataRow nella tabella e si desidera unire entrambe le tabelle in base all’indice:

 public static DataTable MergeTablesByIndex(DataTable t1, DataTable t2) { if (t1 == null || t2 == null) throw new ArgumentNullException("t1 or t2", "Both tables must not be null"); DataTable t3 = t1.Clone(); // first add columns from table1 foreach (DataColumn col in t2.Columns) { string newColumnName = col.ColumnName; int colNum = 1; while (t3.Columns.Contains(newColumnName)) { newColumnName = string.Format("{0}_{1}", col.ColumnName, ++colNum); } t3.Columns.Add(newColumnName, col.DataType); } var mergedRows = t1.AsEnumerable().Zip(t2.AsEnumerable(), (r1, r2) => r1.ItemArray.Concat(r2.ItemArray).ToArray()); foreach (object[] rowFields in mergedRows) t3.Rows.Add(rowFields); return t3; } 

Campione:

 var dt1 = new DataTable(); dt1.Columns.Add("ID", typeof(int)); dt1.Columns.Add("Name", typeof(string)); dt1.Rows.Add(1, "Jon"); var dt2 = new DataTable(); dt2.Columns.Add("Country", typeof(string)); dt2.Rows.Add("US"); var dtMerged = MergeTablesByIndex(dt1, dt2); 

La tabella dei risultati contiene tre colonne ID , Name , Country e una singola riga: 1 Jon US