首页 > 代码库 > Linq之Join操作

Linq之Join操作

1 摘要

文章通过一个简单的实例对Linq中的Join操作进行演示,并在文章的最后对Join操作相关知识点进行简单的总结。


2 实例演示

1) 新建数据库MyTestDB,在数据库中新建数据表tb_Class和tb_Student,两表的定义如下图所示。

                                       

               图1  tb_Class的定义                                                                                     图2  tb_Student的定义  

2) 向数据表tb_Class和tb_Student插入下图所示测试数据。

             

                                图3  tb_Class的测试数据    

   

                                 图4  tb_Student的测试数据          

3) 新建控制台应用程序LinqJoinExp。

4) 本例中使用ADO.NET Entity Framework来访问数据库的数据,使用ADO.NET Entity Framework访问数据库前需要新建ADO.NET实体数据模型,本例中新建的ADO.NET实体数据模型名称为MyTestDB,具体操作可以参考文章《Ado.Net Entities Framework实例》。

5) 下面使用Join分别实现tb_Class左连接tb_Student,tb_Class右连接tb_Student,tb_Class内连接tb_Student操作。详细代码如下所示。

//************************************************************  
//  
// LINQ JOIN类示例代码  
//  
// Author:三五月儿  
//   
// Date:2014/08/02  
//  
// http://blog.csdn.net/yl2isoft  
//  
//************************************************************ 
 
using System;
using System.Linq;
 
namespace LinqJoinExp
{
    class Program
    {
        static void Main(string[] args)
        {
            DoLeftJoin();//左连接
            PrintStar();
            DoRightJoin();//右连接
            PrintStar();
            DoJoin();//连接
        }
 
        /// <summary>
        /// 左连接
        /// </summary>
        public static void DoLeftJoin()
        {
            using (MyTestDBEntities myTestDBEntities = new MyTestDBEntities())
            {
                var leftJoinResult = from c in myTestDBEntities.tb_Class
                               join s in myTestDBEntities.tb_Student
                               on c.classid equals s.classid into joinedResult
                               from jr in joinedResult.DefaultIfEmpty()
                               select new
                               {
                                   ClassId = c.classid,
                                   ClassName = c.classname,
                                   Teacher = c.teacher != null ? c.teacher : string.Empty,
                                   StudentId = jr.id != null ? jr.id : 0,
                                   StudentName = jr.name != null ? jr.name : string.Empty,
                                   StudentAge = jr.age != null ? jr.age : 0,
                                   StudentScore = jr.score != null ? jr.score : 0,
                               };
                foreach (var result in leftJoinResult)
                {
                    Console.WriteLine("ClassId=" + result.ClassId + ","+
                                      "ClassName=" + result.ClassName + ","+
                                      "Teacher=" + result.Teacher + ","+
                                      "StudentId=" + result.StudentId + ","+
                                      "StudentName=" + result.StudentName + ","+
                                      "StudentAge=" + result.StudentAge + ","+
                                      "StudentScore=" + result.StudentScore);
                }
            }
        }
 
        /// <summary>
        /// 右连接
        /// </summary>
        public static void DoRightJoin()
        {
            using (MyTestDBEntities myTestDBEntities = new MyTestDBEntities())
            {
                var rightJoinResult = from s in myTestDBEntities.tb_Student
                               join c in myTestDBEntities.tb_Class
                               on s.classid equals c.classid into joinedResult
                               from jr in joinedResult.DefaultIfEmpty()
                               select new
                               {
                                   StudentId = s.id,
                                   StudentName = s.name,
                                   StudentAge = s.age,
                                   StudentScore = s.score != null ? s.score : 0,
                                   ClassId = jr.classid != null ? jr.classid : 0,
                                   ClassName = jr.classname != null ? jr.classname : string.Empty,
                                   Teacher = jr.teacher != null ? jr.teacher : string.Empty,
                               };
                foreach (var result in rightJoinResult)
                {
                    Console.WriteLine("ClassId=" + result.ClassId + "," +
                                      "ClassName=" + result.ClassName + "," +
                                      "Teacher=" + result.Teacher + "," +
                                      "StudentId=" + result.StudentId + "," +
                                      "StudentName=" + result.StudentName + "," +
                                      "StudentAge=" + result.StudentAge + "," +
                                      "StudentScore=" + result.StudentScore);
                }
             }
        }
 
        /// <summary>
        /// 连接
        /// </summary>
        public static void DoJoin()
        {
            using (MyTestDBEntities myTestDBEntities = new MyTestDBEntities())
            {
                var joinResult = from c in myTestDBEntities.tb_Class
                          join s in myTestDBEntities.tb_Student
                          on c.classid equals s.classid
                          select new
                          {
                               ClassId = c.classid,
                               ClassName = c.classname,
                               Teacher = c.teacher != null ? c.teacher : string.Empty,
                               StudentId = s.id,
                               StudentName = s.name,
                               StudentAge = s.age,
                               StudentScore = s.score != null ? s.score : 0,
                           };
                foreach (var result in joinResult)
                {
                    Console.WriteLine("ClassId=" + result.ClassId + "," +
                                      "ClassName=" + result.ClassName + "," +
                                      "Teacher=" + result.Teacher + "," +
                                      "StudentId=" + result.StudentId + "," +
                                      "StudentName=" + result.StudentName + "," +
                                      "StudentAge=" + result.StudentAge + "," +
                                      "StudentScore=" + result.StudentScore);
                }
            }
        }
 
        /// <summary>
        /// 显示型号分隔符
        /// </summary>
        public static void PrintStar()
        {
            Console.WriteLine("**************************************************************************");
        }
    }
}
6) 运行示例程序,得到下图所示结果。

                     图5     程序运行结果

7) 在阅读程序的运行结果前,我们先来对程序的结果进行一番预测。

首先,对连接的定义(借鉴SQL中的定义,可以参考文章《JOIN操作实例》)做简单的复习。

  • 左外连接:从左表那里返回所有的行以及右表中符合指定的匹配条件的行。
  • 右外连接:从右表那里返回所有的行以及左表中符合指定的匹配条件的行。
  • 内连接:仅当至少有一个同属于两表的行符合连接条件时,内连接才返回行。

根据连接的定义,我们可以得到tb_Class左连接tb_Student,tb_Class右连接tb_Student,tb_Class内连接tb_Student的结果。

表1 tb_Class左连接tb_Student的结果

classid

classname

teacher

id

name

age

classid

score

1

1班

yul

1

zhangs

12

1

NULL

1

1班

yul

4

liulu

12

1

NULL

2

2班

wsp

2

lisi

10

2

85

3

3班

NULL

3

wangwu

11

3

90

14

4班

lisi

NULL

NULL

NULL

NULL

NULL 

表2 tb_Class右连接tb_Student的结果

classid

classname

teacher

id

name

age

classid

score

1

1班

yul

1

zhangs

12

1

NULL

2

2班

wsp

2

lisi

10

2

85

3

3班

NULL

3

wangwu

11

3

90

1

1班

yul

4

liulu

12

1

NULL

NULL

NULL

NULL

5

liujia

12

5

88

表3 tb_Class内连接tb_Student的结果

classid

classname

teacher

id

name

age

classid

score

1

1班

yul

1

zhangs

12

1

NULL

1

1班

yul

4

liulu

12

1

NULL

2

2班

wsp

2

lisi

10

2

85

3

3班

NULL

3

wangwu

11

3

90

将我们根据定义计算得到的结果与程序的运行结果进行对比,两者完全一致。说明,本文的示例程序是正确的,所以,大家完全可以放心的参考本文的示例代码进行学习(文章最后会给出程序源码的下载地址)。这里,有一点需要补充说明一下,根据定义计算得到的结果中,存在大量的NULL值,但是示例程序结果中并不存在NULL值,这是因为示例程序对可能出现NULL值的字段都进行了处理,当某字段值为NULL时,就用0或者空串替换该字段的值,例如:Teacher = c.teacher != null ? c.teacher : string.Empty。

 

3 知识总结

1) 在Linq中,使用Join就可以完成左连接,右连接,内连接的操作。只是,在进行左右连接操作时,还需要借助于Enumerable.DefaultIfEmpty()方法,此方法位于命名空间System.Linq中,是一个扩展方法,该方法的作用是:返回指定序列的元素,如果序列为空时则使用序列元素对应类型的默认值代替,引用和可空类型的默认值为 null。

2) 通过本文所给示例,可以了解到,linq中左连接与右联接的代码实现没有任何区别,因为“tb_Class右连接tb_Student”本质上与“tb_Student左连接tb_Class”完全相同,所以在实现“tb_Class右连接tb_Student”时,本质上仍然使用的是左连接来实现的,因此,linq中的左右联接本质上都是左连接。

3) 最后,再次强调一下连接的定义,毕竟,只有充分理解了定义的基础上才能进行灵活地应用嘛,呵呵。

  • 左外连接:从左表那里返回所有的行以及右表中符合指定的匹配条件的行。
  • 右外连接:从右表那里返回所有的行以及左表中符合指定的匹配条件的行。
  • 内连接:仅当至少有一个同属于两表的行符合连接条件时,内连接才返回行。
好了,就到这里了,886。