没有LINQ的c#完全外部连接两个数据表

本文关键字:数据表 两个 连接 外部 LINQ 没有 | 更新日期: 2023-09-27 18:14:11

感谢您的阅读。

我的目标是:我有两个数据表,我从Excel工作表中读取。数据表具有相同的模式(列A, B, C,…)在Datatable1有相同类型的数据列A, B, C,…在Datatable2)。

我需要通过任意列比较表中的数据(即只比较A列和C列的问题,但我需要保持A列,B列,C列的数据,…, N)。

因为我是从Excel工作表中读取这些,所以不能期望模式。例如,如果加载一组不同的工作表,则比较列可能不同。由于这个原因,我不能使用LINQ,它就像一个硬编码的SQL语句。

我需要执行相当于FULL OUTER JOIN的操作。我试图显示所有的数据,包括从数据表中没有出现在其他数据表中丢失的数据。

我读了一些关于数据关系的知识,但是我不知道如何使用它们。

请提供示例代码。

提前感谢!

没有LINQ的c#完全外部连接两个数据表

给定一对具有任意列数的DataTable s,并给定一个可以从两个DataTable s中创建合理类型的分组值的函数,您可以使用Linq为您完成大部分工作。

让我们从从DataTable s中提取连接键的函数开始。如果只返回一个object[]就好了,但是它们不能很好地进行比较。然而,我们可以用Tuple<object, object>来做到这一点-这些很好地用于此目的。如果你需要更多的列,你可以添加更多的列:P

// Produces a JoinKey as Tuple containing columns 'A' and 'C' (0 and 2)
public Tuple<object, object> GetJoinKey(DataRow row)
{
    return Tuple.Create(row[0], row[2]);
}

现在连接。我们不能直接执行一个完整的外连接,但是我们可以通过两种方式执行外连接并Union结果:

// given DataTables table1 & table2:
var outerjoin = 
    (
        from row1 in table1.AsEnumerable()
        join row2 in table2.AsEnumerable() 
            on GetJoinKey(row1) equals GetJoinKey(row2)
            into matches
        from row2 in matches.DefaultIfEmpty()
        select new { key = GetJoinKey(row1), row1, row2 }
    ).Union(
        from row2 in table2.AsEnumerable()
        join row1 in table1.AsEnumerable()
            on GetJoinKey(row2) equals GetJoinKey(row1)
            into matches
        from row1 in matches.DefaultIfEmpty()
        select new { key = GetJoinKey(row2), row1, row2 }
    );

接下来,您必须创建一个合适的输出格式- DataTable,其中包含来自两个源的所有行,加上一个字段来保存有关键的一些信息:

DataTable result = new DataTable();
// add column for string value of key:
result.Columns.Add("__key", typeof(string));
// add columns from table1:
foreach (var col in table1.Columns.OfType<DataColumn>())
    result.Columns.Add("T1_" + col.ColumnName, col.DataType);
// add columns from table2:
foreach (var col in table2.Columns.OfType<DataColumn>())
    result.Columns.Add("T2_" + col.ColumnName, col.DataType);

最后,从查询中填充表:

var row1def = new object[table1.Columns.Count];
var row2def = new object[table2.Columns.Count];
foreach (var src in outerjoin)
{
    // extract values from each row where present
    var data1 = (src.row1 == null ? row1def : src.row1.ItemArray);
    var data2 = (src.row2 == null ? row2def : src.row2.ItemArray);
    // create row with key string and row values
    result.Rows.Add(new object[] { src.key.ToString() }.Concat(data1).Concat(data2).ToArray());
}

当然,我们可以缩短几个这样的操作,得到一个Linq查询,为我们做99%的工作。如果听起来有趣的话,我把它留给你自己玩。

下面是完整的方法,作为连接键生成器的泛型函数的扩展,使其具有合理的泛型:

public static DataTable FullOuterJoin<T>(this DataTable table1, DataTable table2, Func<DataRow, T> keygen)
{
    // perform inital outer join operation
    var outerjoin = 
        (
            from row1 in table1.AsEnumerable()
            join row2 in table2.AsEnumerable() 
                on keygen(row1) equals keygen(row2)
                into matches
            from row2 in matches.DefaultIfEmpty()
            select new { key = keygen(row1), row1, row2 }
        ).Union(
            from row2 in table2.AsEnumerable()
            join row1 in table1.AsEnumerable()
                on keygen(row2) equals keygen(row1)
                into matches
            from row1 in matches.DefaultIfEmpty()
            select new { key = keygen(row2), row1, row2 }
        );
    // Create result table
    DataTable result = new DataTable();
    result.Columns.Add("__key", typeof(string));
    foreach (var col in table1.Columns.OfType<DataColumn>())
        result.Columns.Add("T1_" + col.ColumnName, col.DataType);
    foreach (var col in table2.Columns.OfType<DataColumn>())
        result.Columns.Add("T2_" + col.ColumnName, col.DataType);
    // fill table from join query
    var row1def = new object[table1.Columns.Count];
    var row2def = new object[table2.Columns.Count];
    foreach (var src in outerjoin)
    {
        // extract values from each row where present
        var data1 = (src.row1 == null ? row1def : src.row1.ItemArray);
        var data2 = (src.row2 == null ? row2def : src.row2.ItemArray);
        // create row with key string and row values
        result.Rows.Add(new object[] { src.key.ToString() }.Concat(data1).Concat(data2).ToArray());
    }
    return result;
}

现在,如果表具有相同的模式(上面不关心),您可以做几乎完全相同的事情-修改结果表生成,只克隆其中一个表,然后在加载循环中添加一些合并逻辑。

这里是上面的一个要点,测试和验证它正在做它所说的。把它放到你的编译器中,看看你得到了什么。