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

C# C完全外部连接两个没有LINQ的数据表,c#,sql,excel,datatable,datarelation,C#,Sql,Excel,Datatable,Datarelation,谢谢你阅读这篇文章 我的目标是: 我从Excel工作表中读取了两个数据表。数据表具有相同的模式列A、B、C、。。。在Datatable1中,列A、B、C、。。。在数据表2中 我需要按任意列比较表中的数据,即仅用于比较A列和C列,但我需要保留A、B、C、…、N列中的数据 因为我是从Excel工作表中阅读这些内容的,所以不能期望出现模式。例如,如果加载一组不同的工作表,则比较列可能会有所不同。由于这个原因,我不能使用LINQ,它就像一个硬编码的SQL语句 我需要执行相当于完全外部联接的操作。我试图显

谢谢你阅读这篇文章

我的目标是: 我从Excel工作表中读取了两个数据表。数据表具有相同的模式列A、B、C、。。。在Datatable1中,列A、B、C、。。。在数据表2中

我需要按任意列比较表中的数据,即仅用于比较A列和C列,但我需要保留A、B、C、…、N列中的数据

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

我需要执行相当于完全外部联接的操作。我试图显示所有数据,包括两个数据表中缺失的数据,而这两个数据表都没有出现在另一个数据表中

我读过一些关于数据关系的书,但我不知道如何使用它们

请提供示例代码

提前谢谢

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

让我们从一个函数开始,从数据表中提取连接键。只返回一个对象[]会很好,但它们的比较不好。不过,我们可以使用元组来实现这一点——这些元组可以很好地实现这一目的。如果您需要更多列,您可以添加更多列: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]);
}
接下来,您必须创建一个合适的输出格式—一个包含来自两个源的所有行的数据表,以及一个用于保存有关键的一些信息的字段:

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);
当然,我们可以缩短其中的几个操作,得到一个Linq查询,它为我们完成了99%的工作。如果听起来有趣的话,我会把它留给你玩

下面是完整的方法,它作为一个扩展,为join key生成器提供了一个泛型函数,使其具有合理的泛型性:

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;
}
现在,如果表具有上面不关心的相同模式,您可以做几乎完全相同的事情-修改结果表生成以仅克隆其中一个表,然后在加载循环中添加一些合并逻辑


通过测试和验证,证明它在做它所说的。把它放到编译器中,看看能得到什么。

这真的是完全连接还是合并?就像硬编码的SQL语句一样@Corey:我需要一个完整的外部联接,显示两个工作表中缺失数据的空值。@MatíasFidemraizer:LINQ语句是由C代码组成的,不能以编程方式修改。可以构建一个字符串SQL语句来匹配遇到的任何工作表架构。@Jesstuart No Comments这是一个很棒的方法!我希望我有足够的声望去投票!这必须是一个静态类吗?在这种情况下使用静态类的优点是什么?我将其设置为静态方法,因为它不使用实例数据,所以当您将其放置在静态类中时,它可以作为DataTable上的扩展方法。。。因此,您可以使用table1.FullOuterJointable2,keygen;而不是表2的FullOuterJointable1,keygen;这对于在完全输出联接中重新生成数据非常有用,但它不会保留原始内容顺序。为了获得顺序,我必须循环遍历每个表,比较键值,并根据比较结果将每个表中的行复制到结果中。我非常感谢你的例子@corey,因为我以前从未使用过元组。元组是很方便的东西,所以我很高兴能向你介绍它们。
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());
}
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;
}