首页 > 代码库 > 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) 最后,再次强调一下连接的定义,毕竟,只有充分理解了定义的基础上才能进行灵活地应用嘛,呵呵。
- 左外连接:从左表那里返回所有的行以及右表中符合指定的匹配条件的行。
- 右外连接:从右表那里返回所有的行以及左表中符合指定的匹配条件的行。
- 内连接:仅当至少有一个同属于两表的行符合连接条件时,内连接才返回行。