”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > LINQ全外连接操作方法详解

LINQ全外连接操作方法详解

发布于2025-03-13
浏览:252

How to Perform a Full Outer Join in LINQ?

LINQ - 全外连接

问题:

如何基于公共键字段在两个对象列表之间执行全外连接,确保两个列表中的所有记录都包含在结果中,即使它们在另一个列表中没有对应的匹配项?

答案:

1. 全外连接的自定义扩展方法

为了在 LINQ 中实现全外连接,我们可以定义一个自定义扩展方法,如下所示:

public static IEnumerable FullOuterJoin(
    this IEnumerable a,
    IEnumerable b,
    Func selectKeyA,
    Func selectKeyB,
    Func projection,
    TA defaultA = default(TA),
    TB defaultB = default(TB),
    IEqualityComparer cmp = null)
{
    cmp = cmp ?? EqualityComparer.Default;
    var alookup = a.ToLookup(selectKeyA, cmp);
    var blookup = b.ToLookup(selectKeyB, cmp);

    var keys = new HashSet(alookup.Select(p => p.Key), cmp);
    keys.UnionWith(blookup.Select(p => p.Key));

    var join = from key in keys
               from xa in alookup[key].DefaultIfEmpty(defaultA)
               from xb in blookup[key].DefaultIfEmpty(defaultB)
               select projection(xa, xb, key);

    return join;
}

2. 实现

在提供的示例中,我们有两个列表:firstNameslastNames。要执行全外连接,我们可以使用以下代码:

var outerJoin = from first in firstNames
                join last in lastNames
                on first.ID equals last.ID
                into temp
                from last in temp.DefaultIfEmpty()
                select new
                {
                    id = first != null ? first.ID : last.ID,
                    firstname = first != null ? first.Name : string.Empty,
                    surname = last != null ? last.Name : string.Empty
                };

3. 解释

此代码使用左外连接,其中 firstNames 中的每条记录都与 lastNames 中的第一条匹配记录连接,如果找不到匹配项,则连接到默认值。into temp 语句创建了一个 lastNames 中匹配记录的临时集合,允许我们应用 DefaultIfEmpty() 以确保包含不匹配的记录。 这实际上实现了左外连接,并非完整意义上的全外连接。 要实现全外连接,需要使用上面自定义的FullOuterJoin扩展方法。

4. 预期输出

全外连接的预期输出为:

ID  FirstName  LastName
--  ---------  --------
1   John       Doe
2   Sue        
3             Smith

5. 可自定义的默认值

提供的扩展方法允许您为不匹配的记录指定默认值。在示例中,我们为不匹配的名称指定了 string.Empty,为不匹配的 ID 指定了负整数。

6. 性能注意事项

全外连接的运行时间为 O(n m),其中 n 和 m 是两个输入列表的长度。这是因为 ToLookup() 操作需要 O(n) 时间,后续操作需要 O(n m) 时间。

7. LINQ 实现

全外连接的这种实现目前不是 LINQ 标准的一部分,但已建议将其包含在未来的版本中。 标准LINQ不直接支持全外连接,需要自定义扩展方法来实现。

最新教程 更多>

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3