关于c#:全外连接,在2个数据表上,带有列列表

Full outer join, on 2 data tables, with a list of columns

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

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

到现在为止我所做的是

  • 使用Intersect获取公共列,并实现IEqualityComparer
  • 使用这些列创建一个新的数据表,以便将两个数据表合并到此新表中
  • 但是,我在第二步遇到了与Linq有关的问题。

    到目前为止,我有:

    获取公用列

    1
    2
        // Get common columns
        var commonColumns = dt1.Columns.OfType().Intersect(dt2.Columns.OfType(), new DataColumnComparer());

    创建新数据表

    1
    2
    3
    4
    5
    6
    7
    8
        // Create the result which is going to be sent to the user
        DataTable result = new DataTable();

        // Add all the columns from both tables
        result.Columns.AddRange(
        dt1.Columns.OfType()
        .Union(dt2.Columns.OfType(), new DataColumnComparer())
        .Select(c => new DataColumn(c.Caption, c.DataType, c.Expression, c.ColumnMapping)).ToArray());

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


    这可能对你有用

    1
    2
    3
    4
    5
    6
    7
    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);


    基于Matthew的答案,我创建了一个接受2个以上数据表的函数。我希望它有助于:

    用途:

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

    以下是函数源:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    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();        
    }


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

    您只需要执行此联接的两个表中的DataTable1DataTable2primarykeys即可。可以将DataTable主键设置为

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

    / /您的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    /// <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;
        }