leftjoinon多条件_详细汇总SQL语句Join连接⽅式与⽤法。↑ 点击上⾯ “时代Java”关注我们,关注新技术,学习新知识!
连接 (Join)
我们知道,连接可以理解为筛选+笛卡尔乘积,即便真实的算法可以做很多优化,基本思想不会变。笛卡尔乘积是固定的,让连接操作变得复杂的,只有筛选操作,也就是筛选条件。根据不同的筛选条件,SQL提供了不同的书写⽅式,帮助程序员明确⽬的、减少意料之外的事情,也增加了学习的难度。只要牢记,各种连接就是筛选条件不同,也就可以很容易地区分各种连接了。
以下挨个介绍常⽤的连接⽅式,同时呈现对应SQL的写法。
⾃然连接 (Natural Join)
两个表中含有相同的「属性」,或称「列」,在笛卡尔乘积的结果集中,筛选这些相同属性相等的情况。
前⽂讲解笛卡尔集合时的例⼦就是⼀种⾃然连接,相同的列是表达教师姓名的两个属性。但是, 从SQL的⾓度说,这个例⼦并不⾃动构成「⾃然连接」,因为两个列的「名称」不同。把instructor表中的列名改为teacher,或把course表中的列名改为name,才可以⾃动构成SQL 意义下的「⾃连接」。
虽然SQL有所规定,但是我们知道,⾃然连接实际划出了⼀类连接,代表「使⽤相等条件筛选」。只要我们明确,「使⽤相等条件筛选」是有直接SQL⽀持的,就可以了。
说了这么多,还没说SQL怎么写。
最简单的是直接使⽤SQL提供的关键字natural join。假设student表和takes表有相同的列ID,则以下两⾏SQL等价:
select name, title from student natural join takes;select name, title from student, takes where student.ID = takes.ID;
关键字natural join相当于⾃动添加了where语句,⾃动检查了两个表的哪⼏个属性相同。
也可以同时操作多个表,多写⼏个natural join就可以了。当然,前提是多个表都存在同样的列。
select name, titlefrom student natural join takes natural join course;
⽆论有多少个表被同时操作,SQL解释器都会到这些表共有的相同列,以这些列的相等为条件,筛选产⽣结果集。
指定连接条件
这样⾃动相同列的操作⼗分⽅便,同时也⼗分危险。要是程序员粗⼼地看漏了⼀些列名,或是对表结构有不正确的预期,可能导致数据库采⽤的相等条件⽐程序员设想的要多。SQL也提供了⼿动指定连接属性的⽅法。
如要连接表⽰选课情况的表takes和课程列表course,指定以属性course_id相等为筛选条件。以下SQL语句等价:
select name, title from takes join course using(course_id);select name, title from takes, course urse_id = urse_id;
也可以指定多个属性,相当于多个筛选条件:
select name, title from takes join course using(course_id, course_name);
<形式的语句可以很⽅便地指定「相等」条件,SQL同样提供了指定其他条件的⽅式,也就是。on语句同样可以指定相等条件,如上⾯的SQL等价于以下SQL:
select name, title from takes join course urse_id = urse_id;
on关键字可以指定任意筛选条件,这就可以实现任意形式的连接,也增加了我们学习的难度。
你可能认为,where关键字可以直接筛选笛卡尔乘积的结果,没有必要使⽤on或using。这是个⼈习惯问题,见仁见智。然⽽,where通常⽤作对结果集最后的筛选,⽽on using专门⽤作连接时候的筛选,遵守⼀些规定,不但可以帮助SQL解释器减轻负担,还可以让⾃⼰的⼯作更加清晰明了。
外连接 (Outer Join)
使⽤「⾃然连接」时,若有⼀个取值没有同时出现在两个表中,连接的结果集中也不会出现这个取值。若有个学⽣没有选任何课程,在学⽣表student中有他,但是在选课表takes中没有他,那么以「学⽣姓名相等」作为连接筛选条件的结果集中也不会出现这个学⽣的名字。
在⼤多数情况下,我们想要这样的效果。但是,我们有时也想保留其中⼀个表的信息,不希望连接之后得到的表信息缺失。这就是「外连接」。
当指定连接⽅式为「外连接」时,即便有的元素没有同时出现在两张表⾥,也会被保留。如⼀个没有选任何课的学⽣,指定外连接后,这个学⽣在学⽣表student中的信息会完整出现在结果集中,⽽相应⾏中来⾃选课表takes的信息则不会出现,以「空」显⽰。可能形式如下:
外连接
SQL语句中对应的关键字是outer join,可以加上前缀left或right或full,表⽰「左连接」、「右连接」和
「全连接」。所谓「左右」的意思是,保留outer join操作符左边或是右边的列的信息。如上图的结果集,就是⼀个「左连接」的结果集,保留了左边student表的信息,⽽没有保留右边takes表的信息,出现在takes表中的信息要是没有出现在student表中,也就不会出现在结果集中。
full代表要同时保留两个表的信息,如果⼀个表中的信息在另⼀个表中没出现,就把没出现的信息显⽰为「空」,道理是相同的。
上图对应SQL语句如下,同样使⽤on来指定连接条件。
select * from student left outer join takes on student.name = takes.name;
相同的结果可以由「顺序相反」的右连接得到:
select * from takes right outer join student on student.name = takes.name;
上⾯两个SQL得到的结果集相同。这提醒我们,⽆论是左连接还是右连接,甚⾄是全连接,重要的是指定好「要保留哪个表的信息」,⽽不是「连接的名字是什么」。
从内连接到外连接
「内连接」是和「外连接」相对的概念,也就是丢弃没有同时出现在两个表中的数据,也就是我们⼀开始介绍的连接⽅式。可以通过内连接理解外连接。
两个表进⾏内连接后,有的⾏满⾜内连接的筛选条件,有的不满⾜。给不满⾜筛选条件的,相应填补空值,得到的就是「外连接」的结果集。
由此,我们可以这样理解:外连接=内连接+Null填补。
外连接条件使⽤where
外连接的筛选条件不能⽤where写。
考虑这样⼀个SQL语句:
select * from student left outer join takes on true where student.ID = takes.ID;
这个SQL语句,连接的结果集是完整的、没有筛选过的笛卡尔乘积。把连接的结果集通过where筛选,最终结果集中⼀定不存在ID不相等的⾏,这不是我们想要的「外连接」结果。
你可以停下来想⼀想,有没有可能通过where实现外连接。当然是不可能的!因为「筛选」不是外连接的最终结果,还有填补空值这⼀步,并且填补空值依赖筛选的结果。
交叉连接
交叉连接就是直接保留笛卡尔乘积的结果集,不做任何处理,没有太多好说的。普通的逗号可以触发交叉连接,如select * from student, takes;。也可以使⽤cross join关键字,如select * from student cross join takes;。
应该不需要再多解释了。
⾃连接
同⼀个表可以当做两个表来使⽤,出现在join操作符的两边,相同的连接操作仍然适⽤。如列出所有的⼯资⼤⼩关系,显⽰所有「同事A⽐同事B⼯资⾼」的情况。
select higher.name as better_employee, lower.name as lower_employeefrom salary as higher join salary as loweron higher.sal > lower.sal;
根据显⽰出来的「⼯资⽐较」,你就可以去打死⽐你⼯资⾼的同事了。
光是可以⽤来打死同事还不太够,「⾃连接」的操作场景更多是⼀种树形结构的序列化⽅式,常⽤来处理「上下级关系」。具有上下级关系的表,通常有⼀个唯⼀表⽰⾃⼰的id,还有⼀个表⽰⽗对象的id。Linux的进程管理器就是⼀个很好的例⼦。下表是在MacOSX终端中输
⼊ps -ej得到的部分结果:
Mac进程表
PID是「进程描述符」,PPID是⽗进程的「进程描述符」。可以看到,⼤多数进程的⽗进程都是1号进程,也就是/sbin/launchd,也就是Mac系统的祖宗进程。这个进程是系统启动之后启动的第⼀个进程,它的⽗进程的「进程描述符」是特殊值0。Mac的进程启动次序⼤概如下图,是个树形结构。
mac boot
像Mac的进程表这样有着树形结构的表就是有「树形结构」的,有唯⼀确定⾃⾝的id,也有「⽗对象」的id。当我们看到某⼀⾏,我们可以通过PPID到这⼀⾏的⽗对象。每个对象都有⼀个⽗对象,这样就构成了⼀个树形结构,根对象的⽗对象⽤⼀个特殊值来表⽰。
sql left join 多表连接可以显⽰出每个进程的⽗进程的命令和⾃⼰的命令,SQL语句如下:
select childmand, parentmandfrom process as child join process as parenton child.ppid = parent.pid;
连接总结
要写带有连接的查询, 需要想清楚这两件事情:
1. 筛选条件是什么?
2. 是否保留只在⼀个表中出现过的信息?
其余的,也就很简单啦!
--
知识分享,时代前⾏!
~~ 时代Java
还有更多好⽂章……
请查看历史⽂章和官⽹,
↓有分享,有收获~
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论