LINQ查询表达式(4) 内部联接 外部连接 参考

  按照关系数据库的说法,“内部联接”产生一个结果集,对于该结果集内第一个集合中的每个元素,只要在第二个集合中存在一个匹配元素,该元素就会出现一次。 如果第一个集合中的某个元素没有匹配元素,则它不会出现在结果集内。 Join 方法(通过 C# 中的 join 子句调用)可实现内联。

  内部连接的4种变体:

  • 简单联接,它基于一个简单的键将来自两个数据源的元素相互关联。
  • 复合联接,它基于一个复合键将来自两个数据源的元素相互关联。 使用复合键(即由多个值组成的键)可以基于多个属性将元素相互关联。
  • 多联接,在其中连续的联接操作被相互拼接在一起。
  • 分组联接

  下面分别描述:

  1. 内部联接:简单键联接
         class Person
            {
                public string FirstName { get; set; }
                public string LastName { get; set; }
            }
    
            class Pet
            {
                public string Name { get; set; }
                public Person Owner { get; set; }
            }
    
            /// <summary>
            /// Simple inner join.
            /// </summary>
            public static void InnerJoinExample()
            {          
                // Create a collection of person-pet pairs. Each element in the collection
                // is an anonymous type containing both the person's name and their pet's name.
                var query = from person in people
                            join pet in pets on person equals pet.Owner
                            select new { OwnerName = person.FirstName, PetName = pet.Name };
         }
  2. 内部联接:复合联接
      与仅仅基于一个属性将元素相互关联不同,使用复合键可基于多个属性来比较元素。 为此,需要为每个集合指定键选择器函数,以便返回一个由要比较的属性组成的匿名类型。 如果给属性加上了标签,则这些属性必须在每个键的匿名类型中都有相同的标签, 而且还必须以相同顺序出现。
    class Employee
            {
                public string FirstName { get; set; }
                public string LastName { get; set; }
                public int EmployeeID { get; set; }
            }
    
            class Student
            {
                public string FirstName { get; set; }
                public string LastName { get; set; }
                public int StudentID { get; set; }
            }
    
     IEnumerable<string> query = from employee in employees
                                            join student in students
                                            on new { employee.FirstName, employee.LastName }
                                            equals new { student.FirstName, student.LastName }
                                            select employee.FirstName + " " + employee.LastName;
  3. 内部连接:多联接
        可以将任意数量的联接操作拼接在一起以执行多联接。 C# 中的每一个 join 子句都可将指定的数据源与前一个联接的结果相互关联。
           class Person
            {
                public string FirstName { get; set; }
                public string LastName { get; set; }
            }
            class Pet
            {
                public string Name { get; set; }
                public Person Owner { get; set; }
            }
            class Cat : Pet
            { }
            class Dog : Pet
            { }
    
                // The first join matches Person and Cat.Owner from the list of people and
                // cats, based on a common Person. The second join matches dogs whose names start
                // with the same letter as the cats that have the same owner.
                var query = from person in people
                            join cat in cats on person equals cat.Owner
                            join dog in dogs on 
                            new { Owner = person, Letter = cat.Name.Substring(0, 1) }
                            equals new { dog.Owner, Letter = dog.Name.Substring(0, 1) }
                            select new { CatName = cat.Name, DogName = dog.Name };
  4. 内部连接:分组联接实现内部联接
         在 query1 中,Person 对象列表基于与 Pet.Owner 属性匹配的 Person 分组联接到 Pet 对象列表。 分组联接创建了一个中间组集合,该集合中的每个组都由一个 Person 对象和匹配的 Pet 对象序列组成。
            class Person
            {
                public string FirstName { get; set; }
                public string LastName { get; set; }
            }
    
            class Pet
            {
                public string Name { get; set; }
                public Person Owner { get; set; }
            }
    
            var query1 = from person in people
                             join pet in pets on person equals pet.Owner into gj
                             from subpet in gj
                             select new { OwnerName = person.FirstName, PetName = subpet.Name };

    分组联接示例:执行分组联接以创建 XML 的示例

    分组联接非常适合于使用 LINQ to XML 来创建 XML。 本例结果选择器函数创建表示已联接对象的 XML 元素,而不是创建匿名类型。

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

      XElement ownersAndPets = new XElement("PetOwners",
                from person in people
                join pet in pets on person equals pet.Owner into gj
                select new XElement("Person",
                    new XAttribute("FirstName", person.FirstName),
                    new XAttribute("LastName", person.LastName),
                    from subpet in gj
                    select new XElement("Pet", subpet.Name)));  

外部连接

  • 外部联接(左外部联接)

     左外部联接是这样一个联接:在其中返回第一个集合的每个元素,而无论该元素在第二个集合中是否具有相关元素。 可以使用 LINQ 执行左通过对分组联接的结果调用方法 DefaultIfEmpty<TSource> 外部联接连接。

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

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

         // 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) };
  •  交差联接
       var crossJoinQuery =
                        from c in categories
                        from p in products
                        select new { c.ID, p.Name };
  • 自定义的联接操作
           var nonEquijoinQuery =
                        from p in products
                        let catIds = from c in categories
                                     select c.ID
                        where catIds.Contains(p.CategoryID) == true
                        select new { Product = p.Name, CategoryID = p.CategoryID };

参考

  [1] MSDN,执行内部连接

  [2] MSDN,执行分组连接