内联接

代码如下

from a in new List<string[]>{
new string[]{"张三","男"},
new string[]{"李四","女"},
new string[]{"王五","男"}
}
join b in new List<string[]>{
new string[]{"张三","英语",""},
new string[]{"张三","语文",""},
new string[]{"李四","数学",""}
}
on a[] equals b[]
select new {User=a,Score=b}

结果的结构如下

注意结果里没有a表的“王五”数据,在内联接查询里,内部联接会生成一个结果集,在该结果集中,第一个集合的每个元素对于第二个集合中的每个匹配元素都会出现一次。 如果第一个集合中的元素没有匹配元素,则它不会出现在结果集中。

总结:内联接用“join 数据源 on 条件"语法,会将左表(即写在前面的表)的每一条记录和右表(即写在后面的表)的每一条记录进行比较,如果左表有x条记录,右表有y条记录,比较会有x*y次比较,但最后的结果不会有x*y条,而是在x*y条里过滤出符合on条件的记录,有点类似“笛卡尔积+条件判断”的操作。

上面的内联接可完全改成两个from操作(进行笛卡尔积求值),结果的结构是完全一样的

from a in new List<string[]>{
new string[]{"张三","男"},
new string[]{"李四","女"},
new string[]{"王五","男"}
}
from b in new List<string[]>{
new string[]{"张三","英语",""},
new string[]{"张三","语文",""},
new string[]{"李四",""}
}
where a[]==b[]
select new {User=a,Score=b}

写到这,可能会问,那所有的内联接操作都改成几个from表就行,还用得着join on的内联接吗?答案是内联接比单纯对几个表进行笛卡尔积求值“效率高很多”,假设有a,b,c三个表,分别为x,y,z条记录,如果用笛卡尔积算法(linq代码如:from a in tab_a from b in tab_b from c in tab_c where ...... select ....),一共会进行x*y*z次连接操作,并对x*y*z条记录进行where过滤;但如果用内联接(linq代码如:from a in tab_a join b in tab_b on ... join c in tab_c on ... select ....),每一次的内联接会基于上一次的结果来进行下一次的操作,即a表和b表进行x*y次操作后,最后可能只得出w条记录(此时的w可能远小于x*y),然后再对c表进行w*z次操作,两者比较x*y*z可能远大于w*z。如果不是a,b,c三个表,而是更多的表进行联接,效率就差距很大了。

组联接

代码如下:

from a in new List<string[]>{
new string[]{"张三","男"},
new string[]{"李四","女"},
new string[]{"王五","男"}
}
join b in new List<string[]>{
new string[]{"张三","英语",""},
new string[]{"张三","语文",""},
new string[]{"李四","数学",""}
}
on a[] equals b[] into b_group
select new {User=a,Score=b_group}

结果的结构如下

注意:此时“王五”出现在了结果里,在组联接里,第一个集合的每个元素都会出现在分组联接的结果集中(无论是否在第二个集合中找到关联元素)。 在未找到任何相关元素的情况下,该元素的相关元素序列为空。 因此,结果选择器有权访问第一个集合的每个元素。 这与非分组联接中的结果选择器不同,后者无法访问第一个集合中在第二个集合中没有匹配项的元素。

总结:内联接用“join 数据源 on 条件 into 新数据源"语法,会以左表(即写在前面的表)的每一条记录为一组,分别和右表(即写在后面的表)的每一条记录进行比较,如果左表有x条记录,右表有条记录,比较会有x*y次比较,但结果只有x组,而每一组可能有<=y条>=0条记录。

如果要对上面的代码进行输出操作,会有两次循环操作

var query=from a in new List<string[]>{
new string[]{"张三","男"},
new string[]{"李四","女"},
new string[]{"王五","男"}
}
join b in new List<string[]>{
new string[]{"张三","英语",""},
new string[]{"张三","语文",""},
new string[]{"李四","数学",""}
}
on a[] equals b[] into b_group
select new {User=a,Score=b_group}; foreach(var p1 in query){
Console.WriteLine($@"{p1.User[0]}的成绩如下:");
foreach(var p2 in p1.Score){
Console.Write($@"---{p2[1]}-{p2[2]}---");
}
Console.WriteLine();
}

结果输出如下:

张三的成绩如下:
---英语-90------语文-70---
李四的成绩如下:
---数学-100---
王五的成绩如下:

可以发现,单是用组联接其实返回的结果在有些情况下是不方便进行处理的,因为要对每一个组再进行循环才能取到我们最终想要的值,下面介绍用“内联接+组联接”来方便的得到我们想要的值

内联接+组联接

代码如下:

from a in new List<string[]>{
new string[]{"张三","男"},
new string[]{"李四","女"},
new string[]{"王五","男"}
}
join b in new List<string[]>{
new string[]{"张三","英语",""},
new string[]{"张三","语文",""},
new string[]{"李四","数学",""}
}
on a[] equals b[] into b_group
from b2 in b_group
select new {User=a,Score=b2}

即在组联接后的新表b_group再次联接:from b2 in b_group

结果的结构如下:

如果细心的朋友会注意到现在的结果和最前面“内联接”一节的结果是一样的。

这样的结果结构相比上一节的组联接的结构更容易获取结果内容,不再需要需要两次循环,取值代码如下

foreach(var p1 in query){
Console.WriteLine($@"{p1.User[0]}-{p1.User[1]}-{p1.Score[1]}-{p1.Score[2]}");
}

输出如下:

张三-男-英语-90
张三-男-语文-70
李四-女-数学-100

对代码稍作修改

from a in new List<string[]>{
new string[]{"张三","男"},
new string[]{"李四","女"},
new string[]{"王五","男"}
}
join b in new List<string[]>{
new string[]{"张三","英语",""},
new string[]{"张三","语文",""},
new string[]{"李四","数学",""}
}
on a[] equals b[] into b_group
from b2 in b_group
select new {User=a,Score=b_group}

只是将

select new {User=a,Score=b2}改成了
select new {User=a,Score=b_group}

结果的结构变成如下

每条结果的结构变成string[]和IGrouping<string,string[]>,不管结果的结构如何,记录里已经没有“王五”的数据。

再改下代码,看看b_group,b2和a的全部结构是怎样的

代码如下:

from a in new List<string[]>{
new string[]{"张三","男"},
new string[]{"李四","女"},
new string[]{"王五","男"}
}
join b in new List<string[]>{
new string[]{"张三","英语",""},
new string[]{"张三","语文",""},
new string[]{"李四",""}
}
on a[] equals b[] into b_group
from b2 in b_group.DefaultIfEmpty()
select new{User=a, Scores=b_group,Score=b2}

结果的结构如下:

左外联接

代码如下

from a in new List<string[]>{
new string[]{"张三","男"},
new string[]{"李四","女"},
new string[]{"王五","男"}
}
join b in new List<string[]>{
new string[]{"张三","英语",""},
new string[]{"张三","语文",""},
new string[]{"李四",""}
}
on a[] equals b[] into b_group
from b2 in b_group.DefaultIfEmpty()
select new{User=a, Score=b2}

只是在“内联接+组联接”的代码上做了一点改动,将from b2 in  b_group改成了from b2 in b_group.DefaultIfEmpty()

结果如下

代码稍作修改,看看内部所有结构

from a in new List<string[]>{
new string[]{"张三","男"},
new string[]{"李四","女"},
new string[]{"王五","男"}
}
join b in new List<string[]>{
new string[]{"张三","英语",""},
new string[]{"张三","语文",""},
new string[]{"李四",""}
}
on a[] equals b[] into b_group
from b2 in b_group.DefaultIfEmpty()
select new{User=a, Scores=b_group,Score=b2}

结果的结构如下

结构和“内联接+组联接"的结构是完成一样的,只是找不到成绩的“王五”也出现在结果集里。

很多资料只写了怎么用“左联接”,但为什么要这么“别扭”写的原因却没有说明,特别是熟悉sql语句的对这种方式很不理解,觉得太绕了。要理解linq,先要抛开之前sql语句的影响,linq既然是c#里对象的sql语句,那我们就要以对象的方式去思考,微软的目的是为了保证linq to object、linq to sql、linq to xml的语法是一样的。先理解linq to object,至于linq to sql最终生成的sql语句是由linq底层的算法来实现的。的下面用图说明下“组联接”--》“内联接+组联接”--》“左外联接“是怎么生成的

深入剖析linq的联接的更多相关文章

  1. LINQ查询表达式(4) - LINQ Join联接

    内部联接 按照关系数据库的说法,“内部联接”产生一个结果集,对于该结果集内第一个集合中的每个元素,只要在第二个集合中存在一个匹配元素,该元素就会出现一次. 如果第一个集合中的某个元素没有匹配元素,则它 ...

  2. LINQ系列:Linq to Object联接操作符

    联接是指将一个数据源对象与另一个数据源对象进行关联或联合的操作.这两个数据源对象通过一个共同的值或属性进行关联. LINQ的联接操作符将包含可匹配(或相同)关键字的两个或多个数据源中的值进行匹配. L ...

  3. Linq世界走一走

    什么是Linq?它是用来做什么的?怎么用? Linq的优点是不管数据源是什么,都可以统一查询.换言之,它是一种包含一套标准查询操作符的查询语言,可以对多个数据源进行查询 ⑴Linq俗称语言集成查询(L ...

  4. 认识LINQ的第一步---从查询表达式开始

    学习和使用C#已经有2个月了,在这两个月的学习中,深刻体会到,C#这门语言还真不适合编程初学者学习,因为它是吸取了很多其他语言,不仅是面向对象,还包括函数式语言的很多特性,导致它变成特性大爆炸的语言. ...

  5. LINQ入门与标准查询运算符

    LINQ的体系结构 查询表达式的完整语法 一.查询表达式必须以from子句开头,以select 或group子句结束.中间可以使用where,orderby ,join,let和其他子句.具有“延迟计 ...

  6. .NET LINQ 联接运算

    联接运算      将两个数据源“联接”就是将一个数据源中的对象与另一个数据源中共享某个通用特性的对象关联起来.      当查询所面向的数据源相互之间具有无法直接领会的关系时,联接就成为一项重要的运 ...

  7. LINQ to Sql系列二 简单查询和联接查询

    这一篇文章主要总结LINQ to sql的简单查询(单表查询)和联接查询(多表查询) 单表查询 需求是我们要输出TClass表中的结果.使用了from-in-select语句,代码如下: public ...

  8. NHibernate3剖析:Query篇之NHibernate.Linq增强查询

    系列引入 NHibernate3.0剖析系列分别从Configuration篇.Mapping篇.Query篇.Session策略篇.应用篇等方面全面揭示NHibernate3.0新特性和应用及其各种 ...

  9. linq性能剖析

    Orcas(VS2008&Framework3.5)给我们带来了很多令人兴奋的新特性,尤其是LINQ的引进,可以说方便了一大批开发 人员和框架设计人员.过去,当我们使用O/RMapping的一 ...

随机推荐

  1. java读取properties文件的几种方法

    一.项目中经常会需要读取配置文件(properties文件),因此读取方法总结如下: 1.通过java.util.Properties读取 Properties p=new Properties(); ...

  2. Answer the questions(回答自己的问题)

    第一章: 问题:我们现在学了这个专业,如果想全面去了解,应该还要学习哪些课程? 回答:其实软件工程只是一个比较大的范畴,以后如果要出去工作,我们还要细分,比如说开发安卓,开发游戏,web架构方面等很多 ...

  3. 基于GUI的小学生四则运算系统

    前言:首先在此感谢我的结对搭档,没有她的帮助和引导绝不会有现在的项目.很高兴和她一起结对完成这个项目.搭档真的是棒棒哒! 一.Coding.Net项目地址: https://git.coding.ne ...

  4. 信安实践——CSRF攻击与防御

    1.实验原理 CSRF(Cross-Site Request Forgery,跨站点伪造请求)是一种网络攻击方式,该攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点,从而在未授权的 ...

  5. CefSharp,Winform程序中加载web网页

    源码地址:https://github.com/cefsharp/CefSharp 开源相关:https://github.com/cefsharp/CefSharp/tree/master/CefS ...

  6. Android表情开发

    Android表情开发 效果图:            源码下载-github:https://github.com/SiberiaDante/EmotionApp (觉得有用的给个星星,支持一下哦)

  7. 蜗牛慢慢爬 LeetCode 19. Remove Nth Node From End of List [Difficulty: Medium]

    题目 Given a linked list, remove the nth node from the end of list and return its head. For example, G ...

  8. teamcity和jmeter结合进行接口自动化测试

    (1)从teamcity官网下载jmeter插件:https://teamcity.jetbrains.com/repository/download/TeamCityPluginsByJetBrai ...

  9. pygame学习笔记(4)——声音

    转载请注明:@小五义 http://www.cnblogs.com/xiaowuyi pygame.mixer是一个用来处理声音的模块,其含义为“混音器”.游戏中对声音的处理一般包括制造声音和播放声音 ...

  10. Windows 常用快捷方式

    gpedit.msc-----组策略sndrec32-----录音机nslookup----- ip地址侦测器explorer------ 打开资源管理器logoff-------注销命令tsshut ...