【问题标题】:Do we need the join clause for outer joins in LINQ?我们是否需要 LINQ 中的外部联接的联接子句?
【发布时间】:2015-01-21 23:58:22
【问题描述】:

This MSDN page 给出了以下外连接示例:

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

class Pet
{
    public string Name { get; set; }
    public Person Owner { get; set; }
}

public static void LeftOuterJoinExample()
{
    Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
    Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
    Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
    Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };

    Pet barley = new Pet { Name = "Barley", Owner = terry };
    Pet boots = new Pet { Name = "Boots", Owner = terry };
    Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
    Pet bluemoon = new Pet { Name = "Blue Moon", Owner = terry };
    Pet daisy = new Pet { Name = "Daisy", Owner = magnus };

    // Create two lists.
    List<Person> people = new List<Person> { magnus, terry, charlotte, arlene };
    List<Pet> pets = new List<Pet> { barley, boots, whiskers, bluemoon, daisy };

    var query = from person in people
                join pet in pets on person equals pet.Owner into gj
                from subpet in gj.DefaultIfEmpty()
                select new { person.FirstName, PetName = (subpet == null ? String.Empty : subpet.Name) };

    foreach (var v in query)
    {
        Console.WriteLine("{0,-15}{1}", v.FirstName + ":", v.PetName);
    }
}

// This code produces the following output: 
// 
// Magnus:         Daisy 
// Terry:          Barley 
// Terry:          Boots 
// Terry:          Blue Moon 
// Charlotte:      Whiskers 
// Arlene:

join 关键字的意义何在?以下查询具有相同的结果。

var query2 = from person in people
             from pet in pets.Where (p => p.Owner.Equals(person)).DefaultIfEmpty()
             select new { person.FirstName, PetName = (pet == null ? String.Empty : pet.Name) };

这两个在功能上是等价的吗?如果不是,它们有何不同?如果是,使用join 关键字进行外连接有什么好处?似乎是编写“where”子句的一种奇特方式。但也许在其他情况下它确实有用?

注意:我意识到 join 子句很可能也用于内部联接、组联接等,我想了解是否有理由将其用于外部联接。

【问题讨论】:

    标签: c# .net linq


    【解决方案1】:

    在 Linq-to-Objects 中,它们将编译为不同的查询,并且可能具有显着不同的性能。

    join 运算符将在内部为“右手”集合创建一个 Lookup,这将使检索匹配结果的速度更快,因为它按哈希码组织项目。

    使用Where 子句将以下列方式执行查找(粗略的伪代码,而不是确切的实现):

    foreach(var left in leftSet)
    {
       foreach(var right in rightSet)
       {
          if(left.key equals right.key)
              yields return result
       }
    }
    

    对于左侧集合中的每个对象,您都在对右侧集合进行全面扫描以查找“匹配”对象。对于大型集合,性能影响可能非常显着。

    请注意,Where 方法可以执行 join 无法实现的“连接”,因为 join 仅支持等连接。

    对于 Ling-to-SQL、Linq-to-EF 等,差异在很大程度上取决于底层查询提供程序。使用 cross-join/where 可能会获得与使用 left join 相似的性能,但您必须尝试并进行测量才能确定。

    【讨论】:

    • 哦,这很好。谢谢你。我会等一天左右,以防其他人想插话,但这或多或少是我所追求的。
    • @Cool.J.Banana 我认为上述答案是正确的,因为 JOIN 表达式针对此类操作进行了优化。
    【解决方案2】:

    您也必须将它用于内部连接,但在许多情况下,在“where”子句中执行此逻辑效率较低。当对具有外键索引而不是简单的对象集合的表执行此操作时,这一点变得更加明显。

    我知道您的问题是关于 LINQ,但由于其中许多概念来自 SQL,因此值得一提:从历史上看,某些 SQL 方言根本没有连接子句 - 您可以用逗号列出表,然后where 子句中的所有逻辑,使用各种特定于供应商的语法 [(+) 用于 Oracle 的表,*==* 用于 MS SQL 上的左/右连接等] 以指示外部连接。

    【讨论】:

    • 谢谢你。我猜只有当我们达到 LINQ 提供程序级别时,SQL 才开始变得相关,但这个问题并不特定于特定的提供程序。在提供者吐出任何(潜在的)SQL 之前,此查询将成为表达式树。我确实同意查询语法很可能是由现有的 SQL 方言提供的,但我对语义不太确定 - 语义与我遇到的任何 SQL 方言都有很大不同。
    猜你喜欢
    • 2013-07-27
    • 1970-01-01
    • 2011-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多