C#中DataTable的内部连接

46

假设T1和T2是具有以下字段的DataTable

T1(CustID, ColX, ColY)

T2(CustID, ColZ)

我需要联合表。

TJ (CustID, ColX, ColY, ColZ)

如何在C#代码中以简单的方式实现这个?谢谢。


这不是 DataTable.Merge 方法吗? - AMissico
6个回答

60
如果您被允许使用LINQ,请查看以下示例。它创建了两个具有整数列的DataTable,用一些记录填充它们,使用LINQ查询将它们连接在一起,并将结果输出到控制台。
    DataTable dt1 = new DataTable();
    dt1.Columns.Add("CustID", typeof(int));
    dt1.Columns.Add("ColX", typeof(int));
    dt1.Columns.Add("ColY", typeof(int));

    DataTable dt2 = new DataTable();
    dt2.Columns.Add("CustID", typeof(int));
    dt2.Columns.Add("ColZ", typeof(int));

    for (int i = 1; i <= 5; i++)
    {
        DataRow row = dt1.NewRow();
        row["CustID"] = i;
        row["ColX"] = 10 + i;
        row["ColY"] = 20 + i;
        dt1.Rows.Add(row);

        row = dt2.NewRow();
        row["CustID"] = i;
        row["ColZ"] = 30 + i;
        dt2.Rows.Add(row);
    }

    var results = from table1 in dt1.AsEnumerable()
                 join table2 in dt2.AsEnumerable() on (int)table1["CustID"] equals (int)table2["CustID"]
                 select new
                 {
                     CustID = (int)table1["CustID"],
                     ColX = (int)table1["ColX"],
                     ColY = (int)table1["ColY"],
                     ColZ = (int)table2["ColZ"]
                 };
    foreach (var item in results)
    {
        Console.WriteLine(String.Format("ID = {0}, ColX = {1}, ColY = {2}, ColZ = {3}", item.CustID, item.ColX, item.ColY, item.ColZ));
    }
    Console.ReadLine();

// Output:
// ID = 1, ColX = 11, ColY = 21, ColZ = 31
// ID = 2, ColX = 12, ColY = 22, ColZ = 32
// ID = 3, ColX = 13, ColY = 23, ColZ = 33
// ID = 4, ColX = 14, ColY = 24, ColZ = 34
// ID = 5, ColX = 15, ColY = 25, ColZ = 35

10
最终这对我起作用了,但我花了一个多小时的时间,一直出现“指定的转换无效”异常,怀疑自己的理智,直到我将(int)table1["field"]更改为Convert.ToInt32(table1["field"])。 - CindyH
2
@CindyH,非常感谢您的评论,它为我节省了时间。我猜这是因为System.Data.DataTypes,其中不包括int,但包括Int32。所有数据类型的列表在此处:https://msdn.microsoft.com/en-us/library/system.data.datacolumn.datatype(v=vs.110).aspx - FrenkyB

35

我想要一个函数,能够在不使用匿名类型选择器定义列的情况下连接表格,但是我很难找到这样的函数。最终我不得不自己动手写。希望这能帮助将来搜索这个问题的人:

private DataTable JoinDataTables(DataTable t1, DataTable t2, params Func<DataRow, DataRow, bool>[] joinOn)
{
    DataTable result = new DataTable();
    foreach (DataColumn col in t1.Columns)
    {
        if (result.Columns[col.ColumnName] == null)
            result.Columns.Add(col.ColumnName, col.DataType);
    }
    foreach (DataColumn col in t2.Columns)
    {
        if (result.Columns[col.ColumnName] == null)
            result.Columns.Add(col.ColumnName, col.DataType);
    }
    foreach (DataRow row1 in t1.Rows)
    {
        var joinRows = t2.AsEnumerable().Where(row2 =>
            {
                foreach (var parameter in joinOn)
                {
                    if (!parameter(row1, row2)) return false;
                }
                return true;
            });
        foreach (DataRow fromRow in joinRows)
        {
            DataRow insertRow = result.NewRow();
            foreach (DataColumn col1 in t1.Columns)
            {
                insertRow[col1.ColumnName] = row1[col1.ColumnName];
            }
            foreach (DataColumn col2 in t2.Columns)
            {
                insertRow[col2.ColumnName] = fromRow[col2.ColumnName];
            }
            result.Rows.Add(insertRow);
        }
    }
    return result;
}

以下是您可能使用此功能的示例:

var test = JoinDataTables(transactionInfo, transactionItems,
               (row1, row2) =>
               row1.Field<int>("TransactionID") == row2.Field<int>("TransactionID"));

需要注意的是:这种方法并没有进行优化,因此在处理超过20k行数据时要谨慎。如果你知道其中一张表比另一张大,尽量将较小的表放在前面,较大的表放在后面。


最简单的方法是不改变代码,只需将所有条件打包成一个连接条件。例如:row1.Field<int>("Id") == row2.Field<int>("Id") || row1.Field<string>("CustId") == row2.Field<string>("CustId") - chkimes
但是,这样你就无法动态指定逻辑运算符了。 - Igor
1
虽然这并不完全正确,但它变得相当丑陋:(useAnd && row1.Field<int>("Id") == row2.Field<int>("Id") && row1.Field<string>("CustId") == row2.Field<string>("CustId")) || (!useAnd && row1.Field<int>("Id") == row2.Field<int>("Id") || row1.Field<string>("CustId") == row2.Field<string>("CustId")) - chkimes
`foreach (Condition condition in conditions) { Func<DataRow, DataRow, bool> join; if (condition.Operator == "=") join = (row1, row2) => row1.Field<string>(condition.LeftOperand) == row2.Field<string>(condition.RightOperand); else join = (row1, row2) => row1.Field<string>(condition.LeftOperand) != row2.Field<string>(condition.RightOperand); joinOn[conditionCounter++] = join; }` - Igor
我能以某种方式将您的建议融入其中吗? - Igor
显示剩余5条评论

5
这是我的代码。虽然不完美,但运行良好。我希望它能帮助到某些人:
    static System.Data.DataTable DtTbl (System.Data.DataTable[] dtToJoin)
    {
        System.Data.DataTable dtJoined = new System.Data.DataTable();

        foreach (System.Data.DataColumn dc in dtToJoin[0].Columns)
            dtJoined.Columns.Add(dc.ColumnName);

        foreach (System.Data.DataTable dt in dtToJoin)
            foreach (System.Data.DataRow dr1 in dt.Rows)
            {
                System.Data.DataRow dr = dtJoined.NewRow();
                foreach (System.Data.DataColumn dc in dtToJoin[0].Columns)
                    dr[dc.ColumnName] = dr1[dc.ColumnName];

                dtJoined.Rows.Add(dr);
            }

        return dtJoined;
    }

3

这个函数将使用已知的连接字段连接两个表,但是除了连接字段之外,它不能允许两个表中具有相同名称的两个字段,一个简单的修改是保存一个带计数器的字典,并为相同名称的字段添加数字。

public static DataTable JoinDataTable(DataTable dataTable1, DataTable dataTable2, string joinField)
{
    var dt = new DataTable();
    var joinTable = from t1 in dataTable1.AsEnumerable()
                            join t2 in dataTable2.AsEnumerable()
                                on t1[joinField] equals t2[joinField]
                            select new { t1, t2 };

    foreach (DataColumn col in dataTable1.Columns)
        dt.Columns.Add(col.ColumnName, typeof(string));

    dt.Columns.Remove(joinField);

    foreach (DataColumn col in dataTable2.Columns)
        dt.Columns.Add(col.ColumnName, typeof(string));

    foreach (var row in joinTable)
    {
        var newRow = dt.NewRow();
        newRow.ItemArray = row.t1.ItemArray.Union(row.t2.ItemArray).ToArray();
        dt.Rows.Add(newRow);
    }
    return dt;
}

0

我尝试以以下方式完成这个任务

public static DataTable JoinTwoTables(DataTable innerTable, DataTable outerTable)
        {
            DataTable resultTable = new DataTable();
            var innerTableColumns = new List<string>();
            foreach (DataColumn column in innerTable.Columns)
            {
                innerTableColumns.Add(column.ColumnName);
                resultTable.Columns.Add(column.ColumnName);
            }

            var outerTableColumns = new List<string>();
            foreach (DataColumn column in outerTable.Columns)
            {
                if (!innerTableColumns.Contains(column.ColumnName))
                {
                    outerTableColumns.Add(column.ColumnName);
                    resultTable.Columns.Add(column.ColumnName);
                }                    
            }

            for (int i = 0; i < innerTable.Rows.Count; i++)
            {
                var row = resultTable.NewRow();
                innerTableColumns.ForEach(x =>
                {
                    row[x] = innerTable.Rows[i][x];
                });
                outerTableColumns.ForEach(x => 
                {
                    row[x] = outerTable.Rows[i][x];
                });
                resultTable.Rows.Add(row);
            }
            return resultTable;
        }

-1
请注意,如果您有一个 DataSet,您需要使用 dataSet.Table[0] 从 DataSet 中获取表格。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接