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的使用场合和特性。

系列博文导航

参考

SQL代码

SQL Join的一些总结的更多相关文章

  1. ORACLE的SQL JOIN方式小结

    在ORACLE数据库中,表与表之间的SQL JOIN方式有多种(不仅表与表,还可以表与视图.物化视图等联结),官方的解释如下所示 A join is a query that combines row ...

  2. SQL JOIN\SQL INNER JOIN 关键字\SQL LEFT JOIN 关键字\SQL RIGHT JOIN 关键字\SQL FULL JOIN 关键字

    SQL join 用于根据两个或多个表中的列之间的关系,从这些表中查询数据. Join 和 Key 有时为了得到完整的结果,我们需要从两个或更多的表中获取结果.我们就需要执行 join. 数据库中的表 ...

  3. 转:画图解释 SQL join 语句

    画图解释 SQL join 语句 我认为 Ligaya Turmelle 的关于SQL联合(join)语句的帖子对于新手开发者来说是份很好的材料.SQL 联合语句好像是基于集合的,用韦恩图来解释咋一看 ...

  4. sql join 用法

    SQL JOIN 的用法   关于sql语句中的连接(join)关键字,是较为常用而又不太容易理解的关键字,下面这个例子给出了一个简单的解释 --建表table1,table2:create tabl ...

  5. [译文] SQL JOIN,你想知道的应该都有

    介绍 这是一篇阐述SQL JOINs的文章. 背景 我是个不喜欢抽象的人,一图胜千言.我在网上查找了所有的关于SQL JOIN的解释,但是没有找到一篇能用图像形象描述的. 有些是有图片的但是他们没有覆 ...

  6. SQL Join各种内联外联说明

    Visual Representation of SQL Joins C.L. Moffatt, 3 Feb 2009 CPOL    4.96 (406 votes) Rate this: This ...

  7. SQL JOIN语法,以及JOIN where 和and区别,还有where和join效率问题。

    语法 join 用于根据两个或多个表中的列之间的关系,从这些表中查询数据. Join 和 Key 有时为了得到完整的结果,我们需要从两个或更多的表中获取结果.我们就需要执行 join. 数据库中的表可 ...

  8. Spark SQL join的三种实现方式

    引言 join是SQL中的常用操作,良好的表结构能够将数据分散到不同的表中,使其符合某种规范(mysql三大范式),可以最大程度的减少数据冗余,更新容错等,而建立表和表之间关系的最佳方式就是join操 ...

  9. SQL Join简单介绍

    前沿 Join是关系型数据库系统的重要操作之一,SQL Server中包含的常用Join:内联接.外联接和交叉联接等. 如果我们想在两个或以上的表获取其中从一个表中的行与另一个表中的行匹配的数据,这时 ...

  10. SQL Join 语句

    SQL Join 语句 SQL 中每一种连接操作都包括一个连接类型和连接条件. 连接类型 决定了如何处理连接条件不匹配的记录. 连接类型 返回结果 inner join 只包含左右表中满足连接条件的记 ...

随机推荐

  1. Android新组件CardView

    Android L以后,新增了一个CardView组件,Google官方应用中有不少地方是使用卡片来展示信息,背后应该就是这个CardView. 使用CardView要引入单独的support包:co ...

  2. 最新版CKEditor在线编辑器的配置方法

    下载地址: http://ckeditor.com/download 使用方法: 在页面<head>中引入ckeditor核心文件ckeditor.js <script type=& ...

  3. Android pop3与imap方式接收邮件(javamail)

    需要下载3个jar包:mail.jar/    activation.jar/    additionnal.jar 1.pop3 /** * 以pop3方式读取邮件,此方法不能读取邮件是否为已读,已 ...

  4. CentOS进不了mysql

    在tty1里输入命令:“mysql -uroot -p+密码” 可以正常进入mysql,但在tty2里就不行了, 提示:Error 1045(28000): Access denied for use ...

  5. mysql入门

    mysql:关系型数据库的一种,用来存储数据: 一个仓库------一个数据库: 一个箱子------ 表: 进入mysql命令行:mysql -uroot -p(密码):查看数据库中所有表:show ...

  6. leetcode 155

    题目描述: Design a stack that supports push, pop, top, and retrieving the minimum element in constant ti ...

  7. kali 渗透的一些笔记

    kali实战笔记 17:55 2016/7/19 by: _Silvers kali系统安装后的配置及美化安装vmwareToolstar zxvf VMwareTools-sfsfsfasfasfs ...

  8. Super Jumping! Jumping! Jumping!

    Nowadays, a kind of chess game called “Super Jumping! Jumping! Jumping!” is very popular in HDU. May ...

  9. (一)GPIO 编程实验 LED 流水灯控制

    7个寄存器 是R1-R16.(当然,里面有很多是分几个模式的,所以总共有37个)类似于单片机的R0-R7. GPXCON,GPXDAT等等是另外的寄存器,应该叫,特殊功能寄存器,类似于单片机的P0,P ...

  10. 为什么这些java接口没有抽象方法?浅谈Java标记接口

    在jdk的源码中,存在这样的一些接口,他们不包含任何的(抽象)方法,但是却广泛的存在. 这种接口我们称之为Mark Interface,也就是标记接口. 这些接口呢,我们不用来实现任何的方法,他们的作 ...