【问题标题】:Full outer join, on 2 data tables, with a list of columns全外连接,在 2 个数据表上,带有列列表
【发布时间】:2013-05-24 11:26:10
【问题描述】:

我有 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());

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

【问题讨论】:

    标签: c# .net linq ado.net


    【解决方案1】:

    这可能对你有用

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

    【讨论】:

    • 如果您能解释一下您的代码,那就太好了。
    【解决方案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();        
    }
    

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

      • 不幸的是,这不适用于我的情况,因为公共列的列表是在运行时提取的,我可以有一个 8 个公共列的列表,我需要根据这些列创建连接这些。
      • 有一个名为flatDataTable 的新表,其中的列从datatable1datatable2 动态提取...
      猜你喜欢
      • 2018-03-04
      • 1970-01-01
      • 2014-11-18
      • 2018-11-19
      • 2018-05-19
      • 2014-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多