如何在c#中使用公共列实现2个或多个数据表的完全外连接

本文关键字:数据表 连接 2个 实现 | 更新日期: 2023-09-27 18:02:57

我需要用一个公共列合并/连接c#上的数据表。

我知道许多关于同一主题的例子和问题。但是我还没有找到任何能回答我问题的。

下面是我正在使用的代码。

代码只允许基于一个数据表的数据的一个公共列。我需要一个公共列,但它需要考虑在另一个数据表中可能使用的任何其他"Account#",并将其添加到公共列中。

代码也只允许合并两个数据表。我需要合并31个数据表,每个月的每一天合并1个数据表。

我有一个数据表,每个月的每一天,dt_docDAY01, dt_docDAY02, dt_docDAY03等。

每个数据表包含一个帐号"account #"和一个存储在指向日期"DAY01"、"DAY02"等列中的余额。

你能告诉我如何更改代码,以便我将包括所有表中的所有帐户吗?

还有,我将如何合并这段代码中的所有数据表,所以我不必运行相同的代码31次。

'string id = "Account#";

                var tableJoinedDAY02 = dt_docDAY01_GROUPED.Clone(); // create columns from table1
                // add columns from table2 except id
                foreach (DataColumn column in dt_docDAY02_GROUPED.Columns)
                {
                    if (column.ColumnName != id)
                        tableJoinedDAY02.Columns.Add(column.ColumnName, column.DataType);
                }
                tableJoinedDAY02.BeginLoadData();
                foreach (DataRow row1 in dt_docDAY01_GROUPED.Rows)
                {
                    foreach (DataRow row2 in dt_docDAY02_GROUPED.Rows)
                    {
                        if (row1.Field<string>(id) == row2.Field<string>(id))
                        {
                            var list = row1.ItemArray.ToList(); // items from table1
                           // add items from table2 except id
                            foreach (DataColumn column in dt_docDAY02_GROUPED.Columns)
                                if (column.ColumnName != id)
                                    list.Add(row2[column]);
                            tableJoinedDAY02.Rows.Add(list.ToArray());
                        }
                    }
                }
                    tableJoinedDAY02.EndLoadData();`

Table1

<>之前账号# | Day011234 | 114567 | 220909 | 33之前

表<>之前账号# | Day021234 | 12[0909] [qh5578 | 990065 | 34之前

Table3

<>之前账号# | Day031234 | 137777 | 44之前

预期结果合并表

Table1

<>之前帐户# | Day01 | Day02 | Day031234 | 11 | 12 | 134567 | 22 | 0 | 00909 | 33 | 34 | 05578 | 0 | 99 | 00065 | 0 | 34 | 07777 | 0 | 0 | 44

如何在c#中使用公共列实现2个或多个数据表的完全外连接

@Infost,你正试图做什么在SQL语言是一个full outer join。在SO上搜索指向这个答案https://stackoverflow.com/a/16832096/97471,我已经适应了超过2个表:

从像这样的MVCE开始:

DataTable table1 = new DataTable();
table1.Columns.Add("Account", typeof(int));
table1.Columns.Add("Day01", typeof(decimal));
table1.Rows.Add(1234, 11);
table1.Rows.Add(4567, 22);
table1.Rows.Add(0909, 33);
DataTable table2 = new DataTable();
table2.Columns.Add("Account", typeof(int));
table2.Columns.Add("Day02", typeof(decimal));
table2.Rows.Add(1234, 12);
table2.Rows.Add(0909, 34);
table2.Rows.Add(5578, 99);
table2.Rows.Add(0065, 34);
DataTable table3 = new DataTable();
table3.Columns.Add("Account", typeof(int));
table3.Columns.Add("Day03", typeof(decimal));
table3.Rows.Add(1234, 13);
table3.Rows.Add(7777, 44);

您可以调用以下函数将它们连接起来:

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

函数来源:

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) { return x.Caption == y.Caption; }
    public int GetHashCode(DataColumn obj) { return obj.Caption.GetHashCode(); }
}

输出为

<>之前帐户Day01 Day02 Day031234 11 12 134567年22日909 33 345578年 99年65年 347777年 44

这需要按如下方式处理,所有的表不能通过魔法连接在一起,让我们取一个较小的样本集:

  1. 表1 (dt1) - Account# | Day01
  2. 表2 (dt2) - Account# | Day02
  3. 表3 (dt3) - Account# | Day03
  4. 表4 (dt4) - Account# | Day04
    dt1.AsEnumerable()
    .Join(dt2.AsEnumerable(), d1 => (int)d1["Account#"], d2 =>            
          (int)d2["Account#"],  
          (d1,d2) => new {Account = (int)d1["Account#"],Day01 =  
                     d1["Day01"],Day02 = d2["Day02"]})
    .Join(dt3.AsEnumerable(), d12 => d12.Account, d3 => (int)d3["Account#"],  
      (d12,d3) => new {d12.Account,d12.Day01,d12.Day02,Day03=d3["Day03"]})
    .Join(dt4.AsEnumerable(), dAll => dAll.Account, d4 =>   
          (int)d4["Account#"],  
          (dAll,d4) => new 
         {dAll.Account,dAll.Day01,dAll.Day02,dAll.Day03,Day04=d4["Day04"]})

上述操作的结果将是IEnumerable<AnonymousType>,其中到目前为止,匿名类型由属性Account,Day01,Day02,Day03,Day04组成,类似地,您可以添加到Day31。还要注意,在第一次join之后,我们如何开始使用作为最后一个Join语句的一部分生成的AnonymousType

这需要转换为DataTable,这将类似于下面线程中发布的代码:

将IEnumerable转换为DataTable

也检查它的转换为DataTable using IEnumerable of Anonymous type,使用Nuget实用程序Fastmember

c#如何将IEnumerable匿名列表转换为数据表