C# DataTable 动态列的内连接

7
我正在尝试以类似于此问题的方式将两个DataTable连接在一起:C#中的DataTable内部连接。我希望输出是一个包含原始表格列的单个'合并'表格。它们都有一个共同的日期时间戳列。给出的答案对于具有固定列的数据表很好,但如果它们是动态创建的,并且可以拥有任意数量的列,那么该如何连接它们呢?例如:
T1 (datestamp, t1Column1, t1Column2, t1ColumnN...)
T2 (datestamp, t2Column1, t2Column2, t2ColumnN...)

我希望加入并创建以下内容:

我想加入并创建以下内容:

J1 (datestamp, t1Column1, t1Column2, t1ColumnN, ..., t2Column1, t2Column2, t2ColumnN...)

这是可能的吗?

你能举个例子,展示一下你想要的结果数据吗? - andres descalzo
我认为 Linq 不是一个选项,因为你需要知道要投影到哪些列名。 - finoutlook
好的,但是您能让我这样做吗?请问您能否给出两个数据表和您想要的结果的示例? - andres descalzo
3个回答

9
我找到了一种解决方案,它不依赖于循环遍历列。它使用了“合并”方法,我之前曾经忽略了它,因为我认为两个表需要具有相同的结构。
首先,您需要在这两个数据表上创建一个主键:
// set primary key
T1.PrimaryKey = new DataColumn[] { T1.Columns["DateStamp"] };
T2.PrimaryKey = new DataColumn[] { T2.Columns["DateStamp"] };

然后将这两个表添加到一个数据集中,以便可以添加关系:

// add both data-tables to data-set
DataSet dsContainer = new DataSet();
dsContainer.Tables.Add(T1);
dsContainer.Tables.Add(T2);

接下来在数据集中添加这两个主键列之间的关系:

// add a relationship between the two timestamp columns
DataRelation relDateStamp = new DataRelation("Date", new DataColumn[] { T1.Columns["DateStamp"] }, new DataColumn[] { T2.Columns["DateStamp"] });
dsContainer.Relations.Add(relDateStamp);

最后,您现在可以将第一个数据表复制到新的“合并”版本中,然后合并第二个数据表:

// populate combined data
DataTable dtCombined = new DataTable();
dtCombined = T1.Copy();
dtCombined.Merge(T2, false, MissingSchemaAction.Add);

注意:Merge方法需要第二个参数为false,否则它只会复制第二个表的结构而不是数据。
这将合并以下表格:
T1 (2012-05-09, 111, 222)
T2 (2012-05-09, 333, 444, 555)

将其合并成基于主键的组合版本:

J1 (2012-05-09, 111, 222, 333, 444, 555)

1

我认为你可以根据链接问题中的答案,使用列的索引而不是列名。或者你可以像这样循环遍历每一行中的项目:

foreach(DataRow row in table.Rows)
{
    foreach(DataColumn column in table.Columns)
    {
        object value = row[column]; // add this to your combined table
    }
}

谢谢 - 我希望避免循环遍历所有列,因为我已经有几个嵌套循环来设置原始表格。如果表格中的数据具有许多不同的日期/键,则可能会变得非常复杂。 - finoutlook

1

看腻了那些不能可靠地模拟SQL的内部连接函数后,我决定在这里自己制作一个:

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

干得好。有没有办法修改它,使得能够在多个“join on”条件之间指定AND或OR运算符? - Igor

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