8

我有 2 个数据表,我不知道它们的数据列列表。此列表必须在运行时提取,并用于完全外连接。

使用这些列时,需要合并2个表之间的列,我需要显示所有数据。

直到现在我正在做的是

  1. 获取常用列,使用 intersect,实现 IEqualityComparer
  2. 使用这些列创建一个新数据表,以便将 2 个数据表合并到这个新表中

但是,我在第二步遇到了 Linq 问题。

直到现在我有:

获取常用列

    // 获取常用列
    var commonColumns = dt1.Columns.OfType().Intersect(dt2.Columns.OfType(), new DataColumnComparer());

创建新数据表

    // 创建要发送给用户的结果
    数据表结果 = 新数据表();

    // 添加两个表中的所有列
    结果.Columns.AddRange(
    dt1.Columns.OfType()
    .Union(dt2.Columns.OfType(), new DataColumnComparer())
    .Select(c => new DataColumn(c.Caption, c.DataType, c.Expression, c.ColumnMapping)).ToArray());

如何从运行时提取的数据列列表中动态获取有效的完全外连接?

4

3 回答 3

5

这可能对你有用

var commonColumns = dt1.Columns.OfType<DataColumn>().Intersect(dt2.Columns.OfType<DataColumn>(), new DataColumnComparer());
        DataTable result = new DataTable();

        dt1.PrimaryKey = commonColumns.ToArray();

        result.Merge(dt1, false, MissingSchemaAction.AddWithKey);
        result.Merge(dt2, false, MissingSchemaAction.AddWithKey);
于 2013-05-30T09:10:46.560 回答
2

根据 Matthew 的回答,我创建了一个接受超过 2 个数据表的函数。我希望它有帮助:

用法:

var table123 = FullOuterJoinDataTables(table1, table2, table3);

下面是函数源:

public DataTable FullOuterJoinDataTables(params DataTable[] datatables) // supports as many datatables as you need.
{
    DataTable result = datatables.First().Clone();

    var commonColumns = result.Columns.OfType<DataColumn>();

    foreach (var dt in datatables.Skip(1))
    {
        commonColumns = commonColumns.Intersect(dt.Columns.OfType<DataColumn>(), new DataColumnComparer());
    }

    result.PrimaryKey = commonColumns.ToArray();

    foreach (var dt in datatables)
    {
        result.Merge(dt, false, MissingSchemaAction.AddWithKey);
    }

    return result;
}

/* also create this class */
public class DataColumnComparer : IEqualityComparer<DataColumn>
{
    public bool Equals(DataColumn x, DataColumn y) => x.Caption == y.Caption;       

    public int GetHashCode(DataColumn obj) => obj.Caption.GetHashCode();        
}
于 2016-08-18T06:19:52.200 回答
1

我也很难得到答案,我正在复制粘贴整个代码。我相信这会对你有所帮助。

您只需要DataTable1,DataTable2primarykeys将在其上执行此联接的两个表。您可以将数据表主键设置为

datatable1.PrimaryKey = new DataColumn[] { captureDT.Columns["Your Key Name"] };

// 你的代码

/// <summary>
    /// Combines the data of two data table into a single data table. The grouping of tables
    /// will be based on the primary key provided for both the tables.
    /// </summary>
    /// <param name="table1"></param>
    /// <param name="table2"></param>
    /// <param name="table1PrimaryKey"></param>
    /// <param name="table2PrimaryKey"></param>
    /// <returns></returns>
    private DataTable DataTablesOuterJoin(DataTable table1, DataTable table2, string table1PrimaryKey, string table2PrimaryKey)
    {
        DataTable flatDataTable = new DataTable();

        foreach (DataColumn column in table2.Columns)
        {
            flatDataTable.Columns.Add(new DataColumn(column.ToString()));
        }
        foreach (DataColumn column in table1.Columns)
        {
            flatDataTable.Columns.Add(new DataColumn(column.ToString()));
        }

        // Retrun empty table with required columns to generate empty extract
        if (table1.Rows.Count <= 0 && table2.Rows.Count <= 0)
        {
            flatDataTable.Columns.Remove(table2PrimaryKey);
            return flatDataTable;
        }

        var dataBaseTable2 = table2.AsEnumerable();
        var groupDataT2toT1 = dataBaseTable2.GroupJoin(table1.AsEnumerable(),
                                br => new { id = br.Field<string>(table2PrimaryKey).Trim().ToLower() },
                                jr => new { id = jr.Field<string>(table1PrimaryKey).Trim().ToLower() },
                                (baseRow, joinRow) => joinRow.DefaultIfEmpty()
                                    .Select(row => new
                                    {
                                        flatRow = baseRow.ItemArray.Concat((row == null) ? new object[table1.Columns.Count] :
                                        row.ItemArray).ToArray()
                                    })).SelectMany(s => s);

        var dataBaseTable1 = table1.AsEnumerable();
        var groupDataT1toT2 = dataBaseTable1.GroupJoin(table2.Select(),
                                br => new { id = br.Field<string>(table1PrimaryKey).Trim().ToLower() },
                                jr => new { id = jr.Field<string>(table2PrimaryKey).Trim().ToLower() },
                                (baseRow, joinRow) => joinRow.DefaultIfEmpty()
                                    .Select(row => new
                                    {
                                        flatRow = (row == null) ? new object[table2.Columns.Count].ToArray().Concat(baseRow.ItemArray).ToArray() :
                                        row.ItemArray.Concat(baseRow.ItemArray).ToArray()
                                    })).SelectMany(s => s);

        // Get the union of both group data to single set
        groupDataT2toT1 = groupDataT2toT1.Union(groupDataT1toT2);

        // Load the grouped data to newly created table 
        foreach (var result in groupDataT2toT1)
        {
            flatDataTable.LoadDataRow(result.flatRow, false);
        }

        // Get the distinct rows only
        IEnumerable rows = flatDataTable.Select().Distinct(DataRowComparer.Default);

        // Create a new distinct table with same structure as flatDataTable
        DataTable distinctFlatDataTable = flatDataTable.Clone();
        distinctFlatDataTable.Rows.Clear();

        // Push all the rows into distinct table.
        // Note: There will be two different columns for primary key1 and primary key2. In grouped rows,
        // primary key1 or primary key2 can have empty values. So copy all the primary key2 values to
        // primary key1 only if primary key1 value is empty and then delete the primary key2. So at last
        // we will get only one perimary key. Please make sure the non-deleted key must be present in 
        foreach (DataRow row in rows)
        {
            if (string.IsNullOrEmpty(row[table1PrimaryKey].ToString()))
                row[table1PrimaryKey] = row[table2PrimaryKey];

            if (string.IsNullOrEmpty(row[CaptureBusDateColumn].ToString()))
                row[CaptureBusDateColumn] = _businessDate;

            if (string.IsNullOrEmpty(row[CaptureUserIDColumn].ToString()))
                row[CaptureUserIDColumn] = row[StatsUserIDColumn];

            distinctFlatDataTable.ImportRow(row);
        }

        // Sort the table based on primary key.
        DataTable sortedFinaltable = (from orderRow in distinctFlatDataTable.AsEnumerable()
                                      orderby orderRow.Field<string>(table1PrimaryKey)
                                      select orderRow).CopyToDataTable();

        // Remove primary key2 as we have already copied it to primary key1 
        sortedFinaltable.Columns.Remove(table2PrimaryKey);

        return ReplaceNulls(sortedFinaltable, "0");
    }

    /// <summary>
    /// Replace all the null values from data table with specified string 
    /// </summary>
    /// <param name="dt"></param>
    /// <param name="replaceStr"></param>
    /// <returns></returns>
    private DataTable ReplaceNulls(DataTable dt, string replaceStr)
    {
        for (int a = 0; a < dt.Rows.Count; a++)
        {
            for (int i = 0; i < dt.Columns.Count; i++)
            {
                if (dt.Rows[a][i] == DBNull.Value)
                {
                    dt.Rows[a][i] = replaceStr;
                }
            }
        }
        return dt;
    }
于 2013-05-29T10:06:33.237 回答