SQL中的join操作总结(非常好)
1.1.1 摘要
Join是关系型数据库系统的重要操作之一,SQL Server中包含的常用Join:内联接、外联接和交叉联接等。如果我们想在两个或以上的表获取其中从一个表中的行与另一个表中的行匹配的数据,这时我们应该考虑使用Join,因为Join具体联接表或函数进行查询的特性
本文将通过具体例子介绍SQL中的各种常用Join的特性和使用场合:
目录
1.1.2 正文
首先我们在tempdb中分别定义三个表College、Student和Apply,具体SQL代码如下:
USE tempdb ---- If database exists the same name datatable deletes it. IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'College') DROP TABLE College; IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Student') DROP TABLE Student; IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Apply') DROP TABLE Apply; ---- Create Database. create table College(cName nvarchar(50), state text, enrollment int); create table Student(sID int, sName nvarchar(50), GPA real, sizeHS int); create table Apply(sID int, cName nvarchar(50), major nvarchar(50), decision text);
Inner join
内联接(Inner join)是最常用的联接类型之一,它查询满足联接谓词的数据。
假设我们要查询申请表Apply中申请学校的相关信息,由于Apply表中包含学校名字我们并不能预知,所以我们可以根据cName来内联接(Inner join)表College和Apply,从而找到Apply表中包含学校的信息。
具体SQL代码如下:
---- Gets college information from college table
---- bases on college name.
SELECT DISTINCT College.cName, College.enrollment
FROM College INNER JOIN
Apply ON College.cName = Apply.cName

图1查询结果
| cName | state | enrollment |
| Stanford | CA | 15000 |
| Berkeley | CA | 36000 |
| MIT | MA | 10000 |
| Cornell | NY | 21000 |
| Harvard | MA | 29000 |
表1 College表中的数据
如上图1所示,我们把Apply表包含的学校信息查询出来了,由于Harvard并没有被查询出来,所以我们知道暂时还没有学生申请Harvard。
内联接(Inner join)满足交换律:“A inner join B” 和 “B inner join A” 是相等的。
Outer join
假设我们想看到所有学校信息;即使是那些没有申请的学校(如:Harvard),这时我们可以使用外部联接(Outer join)进行查询。由于外部联接保存一个或两个输入表的所有行,即使无法找到匹配联接谓词的行。
具体SQL代码如下:
---- Gets all college information
SELECT College.cName, College.state, College.enrollment,
Apply.cName, Apply.major, Apply.decision
FROM College LEFT OUTER JOIN
Apply ON College.cName = Apply.cName

图2左联接查询结果
如上图3所示:由于在Apply表中并没有学生申请Harvard,但是我们通过左联接(left outer join)把所有学校信息查询出来了。
由于左联接(left outer join)产生表College的完全集,而Apply表中匹配的则有值,而不匹配的则以NULL值取代,所以我们知道Apply表中没有学生申请Harvard。
通过左联接查询我们可以获取College的完全集,假设现在我们既要获取College的完全集又要获取Apply的完全集,那么我们可以考虑使用完整外部联接(full outer join)。使用完整外部联接,我们可以查询所有的学校,不管它们是否匹配联接谓词:
---- Gets all information from college and apply table.
SELECT College.cName, College.state, College.enrollment,
Apply.cName, Apply.major, Apply.decision
FROM College FULL OUTER JOIN
Apply ON College.cName = Apply.cName

图3 完整外部联接查询结果
现在我们获取了College和Apply的完全数据集,对于表中匹配的则有值,即使没有找到匹配cName的则以NULL值取代。
下表显示每种外部联接(outer join)匹配时保留数据行的情况:
|
联接类型 |
保留数据行 |
|
A left outer join B |
all A rows |
|
A right outer join B |
all B rows |
|
A full outer join B |
all A and B rows |
表2 外部联接保留数据行
完整外部联接(full outer join)满足交换律:“A full outer join B” 和 “B full outer join A” 是相等的。
Cross join
交叉联接(cross join)执行两个表的笛卡尔积(就是把表A和表B的数据进行一个N*M的组合)。也就是说,它匹配一个表与另一个表中的每一行;我们不能通过使用ON子句在交叉联接指定谓词,虽然我们可以使用WHERE子句来实现相同的结果,这是交叉联接基本上是作为一个内部联接了。
交叉联接相对于内部联接使用率较低,而且两个大表不应该进行交叉联接,因为这将导致一个非常昂贵的操作和一个非常大的结果集。
具体SQL代码如下:
---- College Cross join Apply. SELECT College.cName, College.state, College.enrollment, Apply.cName, Apply.major, Apply.decision FROM College CROSS JOIN Apply

图4 College表和Apply表的行数

图5 交叉联接
现在我们对College和Apply表进行交叉联接,而且生成数据行为College和Apply表行数的笛卡尔积即5 * 20 = 100。
Cross apply
在SQL Server 2005中提供了Cross apply使表可以和表值函数(table-valued functions TVF‘s)结果进行join查询。例如,现在我们想通过函数的结果值和表Student进行查询,这时我们可以使用Cross apply进行查询:
---- Creates a function to get data from Apply base on sID. CREATE FUNCTION dbo.fn_Apply(@sID int) RETURNS @Apply TABLE (cName nvarchar(50), major nvarchar(50)) AS BEGIN INSERT @Apply SELECT cName, major FROM Apply where [sID] = @sID RETURN END ---- Student cross apply function fn_Apply. SELECT Student.sName, Student.GPA, Student.sizeHS, cName, major FROM Student CROSS APPLY dbo.fn_Apply([sID])
我们也可以使用内部联接实现和Cross apply相同的查询功能,具体SQL代码如下:
---- Student INNER JOIN Apply bases on sID. SELECT Student.sName, Student.GPA, Student.sizeHS, cName, major FROM Student INNER JOIN [Apply] ON Student.sID = [Apply].sID

图6 Cross apply查询
Outer apply
在介绍Cross apply和Outer join之后,现在让我们理解Out apply也就不难了,Outer apply使表可以和表值函数(table-valued functions TVF‘s)结果进行join查询,找到匹配值则有值,没有找到匹配值则以NULL表示。
---- Student outer apply function fn_Apply. SELECT Student.sName, Student.GPA, Student.sizeHS, cName, major FROM Student OUTER APPLY dbo.fn_Apply([sID])

图7 Outer apply查询
Inner Join和Cross apply的区别
首先我们知道Inner join是表和表的联接查询,而Cross apply是表和表值函数的联接查询,在前面Cross apply例子中,我们也可以通过Inner join实现相同的查询。
---- Student cross apply function fn_Apply. SET STATISTICS PROFILE ON SET STATISTICS TIME ON SELECT Student.sName, Student.GPA, Student.sizeHS, cName, major FROM Student CROSS APPLY dbo.fn_Apply([sID]) SET STATISTICS PROFILE OFF SET STATISTICS TIME OFF
---- Student INNER JOIN Apply base on sID. SET STATISTICS PROFILE ON SET STATISTICS TIME ON SELECT Student.sName, Student.GPA, Student.sizeHS, cName, major FROM Student INNER JOIN [Apply] ON Student.sID = [Apply].sID SET STATISTICS PROFILE OFF SET STATISTICS TIME OFF
Cross apply查询执行时间:
CPU 时间= 0 毫秒,占用时间= 11 毫秒。
Inner join查询执行时间:
CPU 时间= 0 毫秒,占用时间= 4 毫秒。

图8 执行计划
如图8所示:Cross apply首先执行TVF(table-valued functions),然后对表Studnet进行全表扫描,接着通过遍历sID查找匹配值。
Inner join对表Student和Apply进行全表扫描,然后通过哈希匹配查找匹配的sID值。
通过以上的SQL执行时间和执行计划,我们能不能说Inner join比Cross apply好呢?答案是否定的,如果表的数据量很大,那么Inner join的全表扫描耗费时间和CPU资源就增加了(可通过数据量大的表进行测试)。
虽然大多数采用Cross apply实现的查询,可以通过Inner join实现,但Cross apply可能产生更好的执行计划和更佳的性能,因为它可以在联接执行之前限制集合加入。
Semi-join和Anti-semi-join
Semi-join从一个表中返回的行与另一个表中数据行进行不完全联接查询(查找到匹配的数据行就返回,不再继续查找)。
Anti-semi-join从一个表中返回的行与另一个表中数据行进行不完全联接查询,然后返回不匹配的数据。
不同于其他的联接运算,Semi-join和Anti-semi-join没有明确的语法来实现,但Semi-join和Anti-semi-join在SQL Server中有多种应用场合。我们可以使用EXISTS子来实现Semi-join查询,Not EXISTS来实现Anti-semi-join。现在让我们通过具体的例子说明吧!
假设要求我们找出Apply和Student表中sID匹配的学生信息,这和前面的Inner join查询结果将一样,具体SQL代码如下:
---- Student Semi-join Apply base on sID.
SELECT Student.sName, Student.GPA, Student.sizeHS
----[Apply].cName, [Apply].major
FROM Student
WHERE exists (
SELECT *
from [Apply]
where [Apply].sID = Student.sID
)
我们发现常用的EXISTS子句,原来是通过Left Semi Join实现的,所以说Semi-join在SQL Server中又许多使用场合。

图9 查询结果

图10 执行计划
现在要求我们找出还没有申请学校的学生信息,这时我们立刻反应可以使用NOT EXISTS子句来实现该查询,具体SQL代码如下:
---- Gets student still not apply for school.
SELECT Student.sID, Student.sName, Student.GPA, Student.sizeHS
----[Apply].cName, [Apply].major
FROM Student
WHERE NOT EXISTS (
SELECT *
FROM [Apply]
WHERE [Apply].sID = Student.sID
)
其实,我们常用的NOT EXISTS子句的实现是通过Anti-semi-join,通过执行计划我们发现在查找匹配sID时,SQL使用 Left Anti Semi Join进行查询。

图11 查询结果

图12 执行计划
1.1.3 总结
本文介绍了SQL中常用了联接查询方式:Inner join、Outer join、Cross join和Cross apply的使用场合和特性。
系列博文导航
参考
- http://coolshell.cn/articles/3463.html
- http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html
- http://blogs.msdn.com/b/craigfr/archive/2006/07/19/671712.aspx
SQL代码
SQL中的join操作总结(非常好)的更多相关文章
- SQL点滴2—重温sql语句中的join操作
原文:SQL点滴2-重温sql语句中的join操作 1.join语句 Sql join语句用来合并两个或多个表中的记录.ANSI标准SQL语句中有四种JOIN:INNER,OUTER,LEFTER,R ...
- LINQ TO SQL 中的join(转帖)
http://www.cnblogs.com/ASPNET2008/archive/2008/12/21/1358152.html join对于喜欢写SQL的朋友来说还是比较实用,也比较容易接受的东西 ...
- SQL中inner join、outer join和cross join的区别
对于SQL中inner join.outer join和cross join的区别简介:现有两张表,Table A 是左边的表.Table B 是右边的表.其各有四条记录,其中有两条记录name是相同 ...
- SQL点滴33—SQL中的字符串操作
原文:SQL点滴33-SQL中的字符串操作 计算字符串长度len()用来计算字符串的长度 select sname ,len(sname) from student 字符串转换为大.小写lower() ...
- SQL中关于Join、Inner Join、Left Join、Right Join、Full Join、On、 Where区别
前言: 今天主要的内容是要讲解SQL中关于Join.Inner Join.Left Join.Right Join.Full Join.On. Where区别和用法,不用我说其实前面的这些基本SQL语 ...
- 【转载】SQL中inner join、outer join和cross join的区别
对于SQL中inner join.outer join和cross join的区别很多人不知道,我也是别人问起,才查找资料看了下,跟自己之前的认识差不多, 如果你使用join连表,缺陷的情况下是inn ...
- sql中的join
首先准备数据 有以下数据,三张表:role(角色表).hero(英雄表).skill(技能表),我们以英雄联盟的数据做示例 一个hero对应一个role(我们这里暂定) 一个role可以对应多个her ...
- 重温sql语句中的join操作
1.join语句 Sql join语句用来合并两个或多个表中的记录.ANSI标准SQL语句中有四种JOIN:INNER,OUTER,LEFTER,RIGHT,一个表或视图也可以可以和它自身做JOIN操 ...
- 在MongoDB中使用JOIN操作
SQL与NoSQL最大的不同之一就是不支持JOIN,在传统的数据库中,SQL JOIN子句允许你使用普通的字段,在两个或者是更多表中的组合表中的每行数据.例如,如果你有表books和publisher ...
随机推荐
- Microsoft SQL Server 2012 Internals
http://blog.csdn.net/column/details/learnsqlserver2012.html
- 用yum安装完mysql后没有mysqld的问题
在Centos中用命令 yum install mysql安装数据库,但装完后运行mysqld启动mysql的时候提示找不到,通过 find / | grep mysqld 也没找到mysqld的目录 ...
- Java 8:不要再用循环了 Stream替代for循环
原文:http://www.importnew.com/14841.html 在这篇文章里,我们将会去了解传统循环的一些替代方案.在Java 8的新功能特性中,最棒的特性就是允许我们去表达我们想要完成 ...
- 读懂MySQL执行计划
原文:https://mp.weixin.qq.com/s/-BlLvBKcF-yalELY7XkqaQ 前言 在之前的面试过程中,问到执行计划,有很多童鞋不知道是什么?甚至将执行计划与执行时间认为是 ...
- Idea 创建spring mvc项目时,在add framework support中找不到spring选项
每次创建spring项目最头疼就是spring的配置文件,尤其是配置头信息.通过IDEA工具可以很好辅助完整这些工作. 先说下这个选项的作用:其作用就是利用IDEA自身提供的功能帮你完成一些sprin ...
- class"org.apache.commons.dbcp.BasicDataSource"not found出错的解决办法
法1:在项目上右击build path->configure build path..->add library..->MyEclipse Libraries选中Spring 2.0 ...
- iOS:quartz2D绘图(处理图像,绘制图像并添加水印)
绘制图像既可以重写drawRect:方法并在该方法中绘制,也可以不用重写该方法,它有封装好的函数获取自己的图像绘制上下文,即UIGraphicsBeginImageContext(CGSize siz ...
- 提升JavaScript递归效率:Memoization技术详解[转载]
递归是拖慢脚本运行速度的大敌之一,太多的递归会让浏览器变得越来越慢直到死掉或者莫名其妙的突然自动退出.这里我们可以通过memoization技术来替代函数中太多的递归调用,提升JavaScript效率 ...
- ubuntu中文设置方法
1.首先打开ubuntu设置(右上角的齿轮),或者直接在左面找到设置,如下图所示. 2.然后点[language support]设置,如下图所示. 3.在弹出的窗口再点击[install]设置操作, ...
- springmvc怎么重定向,从一个controller跳到另一个controller
第一种情况,不带参数跳转: 方法一:使用ModelAndView return new ModelAndView("redirect:/toList"); 这样可以重定向到toL ...