linq的内连接,分组连接,左外连接

本文介绍了LINQ中的连接操作,包括内连接、分组连接和左外连接。内连接通过join字句和join方法获取两个对象的交集。分组连接使用into关键字,生成左侧源序列与右侧源序列的分层关联结果。而左外连接结合DefaultIfEmpty方法,确保返回左侧序列的所有元素,即使右侧无匹配项。文中通过学生和老师类的实例代码详细解析了各种连接操作的用法和结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

通用类和字段

定义老师类和学生类,学生类中有老师的ID

 public class Student
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public int TeacherID { get; set; }
        }
        public class Teacher
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }

定义一个连接后的数据类

  public class StuTea
        {
            public int StuId { get; set; }
            public string StuName { get; set; }
            public int TeaId { get; set; }
            public string TeaName { get; set; }

            public override string ToString()
            {
                return string.Format("学生id:{0},学生name:{1},老师id:{2},老师name:{3}",StuId,StuName,TeaId,TeaName);
            }
        }
          public class StuTeaG
        {
            public Student Stu { get; set; }
            public Teacher Tea { get; set; }
           
            public override string ToString()
            {
                return string.Format("学生id:{0},学生name:{1},老师id:{2},老师name:{3}",
                    Stu==null?-1: Stu.Id,Stu==null?"": Stu.Name, Tea == null?-1: Tea.Id, Tea == null?"": Tea.Name);
            }
        }

初始化学生数据和老师数据

   public static List<Student> stus = new List<Student>()
        {
            new Student(){Id=1,Name="张三",TeacherID=1},
             new Student(){Id=2,Name="李四",TeacherID=1},
              new Student(){Id=3,Name="王五",TeacherID=2},
               new Student(){Id=4,Name="赵六",TeacherID=3},
        };

        public static List<Teacher> teas = new List<Teacher>()
        {
              new Teacher(){Id=1,Name="刘老师"},
                new Teacher(){Id=2,Name="徐老师"},
                 new Teacher(){Id=4,Name="杨老师"},
        };

内连接

join字句

  //内连接
            var stuTeas = from stu in stus
                         join tea in teas on stu.TeacherID equals tea.Id
                         select new StuTea
                         {
                             StuId = stu.Id,
                             StuName = stu.Name,
                             TeaId = tea.Id,
                             TeaName = tea.Name
                         };
            foreach (var stuTea in stuTeas)
            {
                Console.WriteLine(stuTea);
            }

join方法

//使用join方法
   var stuTeas = stus.Join(teas, stu => stu.TeacherID, tea => tea.Id, (stu, tea) => new StuTea
            {
                StuId = stu.Id,
                StuName = stu.Name,
                TeaId = tea.Id,
                TeaName = tea.Name
            });
            foreach (var stuTea in stuTeas)
            {
                Console.WriteLine(stuTea);
            }

运行结果:


学生id:1,学生name:张三,老师id:1,老师name:刘老师
学生id:2,学生name:李四,老师id:1,老师name:刘老师
学生id:3,学生name:王五,老师id:2,老师name:徐老师

内连接会取两个对象的交集,由于赵六的teacherId是3,找不到对应的老师。所以最后查询的结果中没有这个学生

分组连接

含有 into 表达式的 join 子句称为分组联接。分组联接会生成分层的结果序列,该序列将左侧源序列中的元素与右侧源序列中的一个或多个匹配元素相关联。

join子句

var stuTeas = from stu in stus
                          join tea in teas on stu.TeacherID equals tea.Id into gs                         
                          select new
                          {
                              Stu = stu,
                              Teas = gs

                          };
            foreach (var stuTea in stuTeas)
            {
                Console.WriteLine(stuTea.Stu.Id+stuTea.Stu.Name);
                foreach (var tea in stuTea.Teas)
                {
                    Console.WriteLine("    "+tea.Id+tea.Name);
                }
            }

GroupJoin方法

 var stuTeas = stus.GroupJoin(teas, stu => stu.TeacherID, tea => tea.Id,
                (stu, teas) => new
                {
                    StuId = stu.Id,
                    StuName = stu.Name,
                    teas = teas,

                });

            foreach (var item in stuTeas)
            {
                Console.WriteLine(item.StuId + item.StuName);
                foreach (var tea in item.teas)
                {
                    Console.WriteLine("     " + tea.Id + tea.Name);
                }
            }

运行结果:

1张三
    1刘老师
2李四
    1刘老师
3王五
    2徐老师
4赵六

左外连接

在左外部联接中,将返回左侧源序列中的所有元素,即使右侧序列中没有其匹配元素也是如此。 若要在 LINQ 中执行左外部联接,请结合使用 DefaultIfEmpty 方法与分组联接,指定要在某个左侧元素不具有匹配元素时生成的默认右侧元素。不使用DefaultIfEmpty会和内联效果相同

以学生查询

代码stu

  var stuTeas = from stu in stus
                          join tea in teas on stu.TeacherID equals tea.Id into gs
                          from g in gs.DefaultIfEmpty()
                          select new StuTea
                          {
                             
                              StuId = stu.Id,
                              StuName = stu.Name,
                              TeaId=g==null?-1:g.Id,
                              TeaName= g == null ? "" : g.Name
                          };
            foreach (var stuTea in stuTeas)
            {
                Console.WriteLine(stuTea);
            }

运行结果

学生id:1,学生name:张三,老师id:1,老师name:刘老师
学生id:2,学生name:李四,老师id:1,老师name:刘老师
学生id:3,学生name:王五,老师id:2,老师name:徐老师
学生id:4,学生name:赵六,老师id:-1,老师name:

解析外联代码1

代码stu2

var stuTeas = from stu in stus
                          join tea in teas on stu.TeacherID equals tea.Id into gs
                          from g in gs.DefaultIfEmpty()
                          select new StuTeaG
                          {
                              Stu = stu,
                              Tea = g
                              
                          };
            foreach (var stuTea in stuTeas)
            {
                Console.WriteLine(stuTea);
            }

代码stu1代码stu2运行结果相同,into gs这个gs存放的就是和stu匹配的tea对象。

DefaultIfEmpty()

如果序列为空,则返回一个具有默认值的单例类集合。
详情可见msdn
在外联查询中的表现,我们去掉gs后的DefaultIfEmpty(),再次运行程序

   var stuTeas = from stu in stus
                          join tea in teas on stu.TeacherID equals tea.Id into gs
                          from g in gs
                          select new StuTea
                          {

                              StuId = stu.Id,
                              StuName = stu.Name,
                              TeaId = g == null ? -1 : g.Id,
                              TeaName = g == null ? "" : g.Name
                          };
            foreach (var stuTea in stuTeas)
            {
                Console.WriteLine(stuTea);
            }

结果为

学生id:1,学生name:张三,老师id:1,老师name:刘老师
学生id:2,学生name:李四,老师id:1,老师name:刘老师
学生id:3,学生name:王五,老师id:2,老师name:徐老师

以老师查询

  //外连接
            var stuTeas = from tea in teas
                          join stu in stus on tea.Id equals stu.TeacherID into gs
                          from g in gs.DefaultIfEmpty()
                          select new StuTea
                          {
                              StuId = g == null ? -1 :g.Id,
                              StuName = g == null ? "" :g.Name,
                              TeaId =  tea.Id,
                              TeaName =  tea.Name
                          };
            foreach (var stuTea in stuTeas)
            {
                Console.WriteLine(stuTea);
            }

运行结果

学生id:1,学生name:张三,老师id:1,老师name:刘老师
学生id:2,学生name:李四,老师id:1,老师name:刘老师
学生id:3,学生name:王五,老师id:2,老师name:徐老师
学生id:-1,学生name:,老师id:4,老师name:杨老师

以老师为例查询代码解析

这个更容易看出,gs中存放的就是tea.Id ==stu.TeacherID的所有stu对象


            var stuTeas = from tea in teas
                          join stu in stus on tea.Id equals stu.TeacherID into gs
                          select new
                          {
                              Stus = gs,
                              Tea = tea

                          };
            foreach (var stuTea in stuTeas)
            {
                Console.WriteLine(stuTea.Tea.Id + stuTea.Tea.Name);
                foreach (var stu in stuTea.Stus)
                {
                    Console.WriteLine("    " + stu.Id + stu.Name);
                }
            }

运行结果

1刘老师
    1张三
    2李四
2徐老师
    3王五
4杨老师

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值