0

我有一个问题,想加入 2 个具有相同列的表。表 1 有 Name、LastName 列和许多其他列,表 2 有 Name、Comment 和许多其他列。我想将它们与名称列一起加入,结果应该是名称、姓氏、评论和其他列。我尝试使用左外 Linq,但不知道如何编写 select new,因为不知道我还有多少其他列。

我的表1:

Name1   LastName ...
Niki   Row      ...
Hube   Slang    ...
Koke   Mi       ... 
...    ...      ...
...    ...      ...

表 2:

Name  Comment   ...
Koke   "Hello"  ...
Niki   "Hi"     ...

结果应该是:

Name   LastName   Comment ...
Niki    Row        "Hi"   ...
Hube    Sland             ...
Koke    Mi         "Hello"...
...     ...               ...

所以我试图将这些行相互连接起来。但是它说 table1 中的数组比 table 2 中的数组长。还有其他方法可以加入吗?

        foreach (DataRow tbE in Table1.Rows)
        {
            foreach (DataRow opT in Table2.Rows)
            {
                if (tbE["Name"].ToString() == opT["Name"].ToString())
                {
                    var row = Result.NewRow();
                    row.ItemArray = tbE.ItemArray
                                       .Concat(opT.ItemArray).ToArray();

                    Result.Rows.Add(row);
                }
                else
                    Result.ImportRow(tbE);

            }
        } 
        Result.Columns.Remove(Name); 
4

2 回答 2

2

你可以在这里使用我最近为另一个问题从头开始编写的这个方法(所以它没有真正经过测试)。它允许通过一个公共键合并多个表。如果没有指定键,它将只使用默认DataTable.Merge方法:

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[] { Table1, Table2 };
tables.MergeAll("Name");

编辑:这是来自调试器的屏幕截图以及您的示例数据:

在此处输入图像描述

所以它有效:)

样本数据和测试在这里:

var Table1 = new DataTable();
var Table2 = new DataTable();
Table1.Columns.Add("Name");
Table1.Columns.Add("LastName");

Table2.Columns.Add("Name");
Table2.Columns.Add("Comment");

Table1.Rows.Add("Niki", "Row");
Table1.Rows.Add("Hube", "Slang");
Table1.Rows.Add("Koke", "Mi");

Table2.Rows.Add("Koke", "Hello");
Table2.Rows.Add("Niki", "Hi");

var tables = new DataTable[] { Table1, Table2 };
DataTable merged = tables.MergeAll("Name");
于 2012-10-31T11:22:10.113 回答
1

这是我的一点贡献。此部分代码可用于连接指定列名上的任意两个 DataTable。(您不需要知道其余列)以下是一些功能:

  1. 生成的 DataTable 不会有用于连接的重复列。例如,如果您加入“名称”列,最后您将只有一个“名称”列,而不是每个表中的一个副本。
  2. 如果存在未在连接中使用的重复列,则将通过在末尾附加“_2”来重命名第二个表中的重复列。它可以以其他方式表现,只需更改那部分代码。
  3. 支持多个连接列。为此,为它们创建了一个 JoinKey 类,以便 LINQ 进行比较。
  4. 这段代码是我在网上找到的代码和我的反复试验的混合体。我是LINQ的新手,请随意批评~

    public class JoinKey
    {
        List<object> objects { get; set; }
    
        public JoinKey(List<object> objects)
        {
            this.objects = objects;
        }
    
        public override bool Equals(object obj)
        {
            if (obj == null || obj.GetType() != typeof(JoinKey))
                return false;
            return objects.SequenceEqual(((JoinKey)obj).objects);
        }
    
        public override int GetHashCode()
        {
            int hash = 0;
            foreach (var foo in objects)
            {
                hash = hash * 31 + foo.GetHashCode();
            }
            return hash;
        }
    }
    
    public enum JoinType
    {
        Inner = 0,
        Left = 1
    }
    
        //Joins two tables and spits out the joined new DataTable. Tables are joined on onCol column names
        //If the right table has column name clashes with the left column, the column names will be appended "_2" and added to joined table
        public static DataTable Join(DataTable left, DataTable right, JoinType joinType, params string[] onCol)
        {
            Func<DataRow, object> getKey = (row) =>
            {
                return new JoinKey(onCol.Select(str => row[str]).ToList());
            };
            var dt = new DataTable(left.TableName);
            var colNumbersToRemove = new List<int>();
            //Populate the columns
            foreach (DataColumn col in left.Columns)
            {
                if (dt.Columns[col.ColumnName] == null)
                    dt.Columns.Add(new DataColumn(col.ColumnName, col.DataType, col.Expression, col.ColumnMapping));
            }
            for (int colIdx = 0; colIdx < right.Columns.Count; ++colIdx)
            {
                var col = right.Columns[colIdx];
                //if this is joined column, it will be removed.
                if (onCol.Contains(col.ColumnName))
                {
                    colNumbersToRemove.Add(colIdx);
                }
                else
                {
                    //if this is duplicate column, it will be renamed.
                    if (dt.Columns[col.ColumnName] != null)
                    {
                        col.ColumnName += "_2";
                    }
                    dt.Columns.Add(new DataColumn(col.ColumnName, col.DataType, col.Expression, col.ColumnMapping));
                }
            }
    
            if (joinType == JoinType.Left)
            {
                var res = from l in left.AsEnumerable()
                          join r in right.AsEnumerable()
                          on getKey(l) equals getKey(r) into temp
                          from r in temp.DefaultIfEmpty()
                          select l.ItemArray.Concat(((r == null) ? (right.NewRow().ItemArray) : r.ItemArray).Minus(colNumbersToRemove)).ToArray();
                foreach (object[] values in res)
                    dt.Rows.Add(values);
            }
            else
            {
                //Inner Join
                var res = from l in left.AsEnumerable()
                          join r in right.AsEnumerable()
                          on getKey(l) equals getKey(r) into temp
                          from r in temp
                          select l.ItemArray.Concat(((r == null) ? (right.NewRow().ItemArray) : r.ItemArray).Minus(colNumbersToRemove)).ToArray();
                foreach (object[] values in res)
                    dt.Rows.Add(values);
            }
            return dt;
        }
    
于 2017-11-30T15:31:47.057 回答