Подтвердить что ты не робот

Объединение n DataTables в единую таблицу данных

Все, есть некоторые вопросы по этому поводу, но я не могу извлечь достаточную информацию для решения проблемы для моего дела. Я извлекаю неизвестное количество таблиц в SQL Server "Tab1", "Tab2", "Tab3",..., "TabN". Столбцы в этих таблицах отличаются друг от друга, но определения строк одинаковы. Мне нужно вывести все данные с сервера на N DataTable, а затем объединить их, чтобы сформировать один DataTable. То, что я делаю в настоящее время,

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

Это объединяет DataTable, но они выровнены по очкам (заполнение пустых ячеек на добавленный набор данных). Я мог бы добавить столбцы нового DataTable через цикл; , но есть ли более простой способ сделать это (возможно, используя LINQ)?

Спасибо за ваше время.

Изменить. Чтобы предоставить примеры наборов данных.

Мне нужно

Full DataTable

Отдельные таблицы

Tabs

После первой операции Merge у меня есть следующий

After Merge

Еще раз спасибо.

4b9b3361

Ответ 1

Таблица повторяет первичные ключи после Merge, потому что первичный ключ не был определен. Поэтому либо укажите PK, либо попробуйте этот метод здесь, который я написал с нуля (поэтому он не протестирован):

public static DataTable MergeAll(this IList<DataTable> 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;
}

Вы можете вызвать его следующим образом:

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

Использовали данные этого образца:

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');

После DataTable.Merge в MergeAll:

enter image description here

После некоторых изменений для объединения строк в MergeAll:

enter image description here


Update

Поскольку этот вопрос возник в одном из комментариев, если единственное отношение между двумя таблицами - это индекс DataRow в таблице, и вы хотите объединить обе таблицы в соответствии с индексом:

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

Пример:

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

Таблица результатов содержит три столбца ID, Name, Country и одну строку: 1 Jon US