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

图3左联接查询结果

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

(0)

相关推荐

  • sql join on 用法

    A left join B 的连接的记录数与A表的记录数同A right join B 的连接的记录数与B表的记录数同 A left join B 等价B right join A table A:Field_K, Field_A1 a3 b4 ctable B:Field_K, Field_B1 x2 y4 zselect a.Field_K, a.Field_A, b.Field_K, b.Field_B from a left join b on a.Field_K=b.Field_KFi

  • SQL中JOIN和UNION区别、用法及示例介绍

    1.JOIN和UNION区别 join 是两张表做交连后里面条件相同的部分记录产生一个记录集, union是产生的两个记录集(字段要一样的)并在一起,成为一个新的记录集 . JOIN用于按照ON条件联接两个表,主要有四种: INNER JOIN:内部联接两个表中的记录,仅当至少有一个同属于两表的行符合联接条件时,内联接才返回行.我理解的是只要记录不符合ON条件,就不会显示在结果集内. LEFT JOIN / LEFT OUTER JOIN:外部联接两个表中的记录,并包含左表中的全部记录.如果左表

  • SQL中的left join right join

    以下是它们的共同点: 1. 关于左右表的概念.左表指的是在SQL语句中排在left join左边的表,右表指的是排在left join右边的表. 2. 在拼成的大表中,左表排在左边,右表排在右边. 3. on条件语句最好用=号对两表相应的主外键进行连接.当然,也可以用其他操作符,如>, <, 来连接两表的任一字段,此时的关系将非常复杂,连接后的记录数也随之而变得不确定.如果在一些特殊的场合中需要用到这种方式,必须通过简单的实例加以确认,否则,连接结果很可能不是我们所想要的! 4. on条件语句

  • MySQL JOIN之完全用法

    外联接.外联接可以是左向外联接.右向外联接或完整外部联接. 在 FROM 子句中指定外联接时,可以由下列几组关键字中的一组指定: LEFT JOIN 或 LEFT OUTER JOIN. 左向外联接的结果集包括 LEFT OUTER 子句中指定的左表的所有行,而不仅仅 是联接列所匹配的行.如果左表的某行在右表中没有匹配行,则在相关联的结果集行中 右表的所有选择列表列均为空值. RIGHT JOIN 或 RIGHT OUTER JOIN. 右向外联接是左向外联接的反向联接.将返回右表的所有行.如果

  • SQL的Join使用图解教程

    对于SQL的Join,在学习起来可能是比较乱的.我们知道,SQL的Join语法有很多inner的,有outer的,有left的,有时候,对于Select出来的结果集是什么样子有点不是很清楚.Coding Horror上有一篇文章(实在不清楚为什么Coding Horror也被墙)通过 文氏图 Venn diagrams 解释了SQL的Join.我觉得清楚易懂,转过来. 假设我们有两张表. Table A 是左边的表. Table B 是右边的表. 其各有四条记录,其中有两条记录是相同的,如下所示

  • sql left join 命令详解

    给个通俗的解释吧. 例表a aid adate 1 a1 2 a2 3 a3 表b bid bdate 1 b1 2 b2 4 b4 两个表a,b相连接,要取出id相同的字段 select * from a inner join b on a.aid = b.bid这是仅取出匹配的数据. 此时的取出的是: 1 a1 b1 2 a2 b2 那么left join 指: select * from a left join b on a.aid = b.bid 首先取出a表中所有数据,然后再加上与a,

  • SQL Join的一些总结(实例)

    1.1.1 摘要 Join是关系型数据库系统的重要操作之一,SQL Server中包含的常用Join:内联接.外联接和交叉联接等.如果我们想在两个或以上的表获取其中从一个表中的行与另一个表中的行匹配的数据,这时我们应该考虑使用Join,因为Join具体联接表或函数进行查询的特性 本文将通过具体例子介绍SQL中的各种常用Join的特性和使用场合: 1.1.2 正文 首先我们在tempdb中分别定义三个表College.Student和Apply,具体SQL代码如下: 复制代码 代码如下: USE

  • SQL JOIN 连接详细介绍及简单使用实例

    SQL JOIN 连接 SQL JOIN 子句用于把来自两个或多个表的行结合起来,基于这些表之间的共同字段. 最常见的 JOIN 类型:SQL INNER JOIN(简单的 JOIN). SQL INNER JOIN 从多个表中返回满足 JOIN 条件的所有行. 让我们看看选自 "Orders" 表的数据: OrderID CustomerID OrderDate 10308 2 1996-09-18 10309 37 1996-09-19 10310 77 1996-09-20 然后

  • MySQL表LEFT JOIN左连接与RIGHT JOIN右连接的实例教程

    LEFT JOIN 语法用法与实例 MySQL LEFT JOIN 语法 SQL(MySQL) LEFT JOIN 会取得左表(table1)全部记录,即使右表(table2)并无对应匹配记录.LEFT JOIN 基本语法如下: ... FROM table1 LEFT JOIN table2 ON condition ... MySQL LEFT JOIN 用法实例 下面是两个原始数据表: article 文章表: user 用户表: 我们列出所有的文章及对应的所属用户,即使没有用户的文章也列

  • Mysql inner join on的用法实例(必看)

    语法规则 SELECT column_name(s) FROM table_name1 INNER JOIN table_name2 ON table_name1.column_name=table_name2.column_name 先创建两个表,1.用户,2.用户类别 用户表 CREATE TABLE `user` ( `id` int(32) NOT NULL AUTO_INCREMENT, `name` varchar(16) NOT NULL, `kindid` int(32) NOT

  • springboot整合mybatis将sql打印到日志的实例详解

    在前台请求数据的时候,sql语句一直都是打印到控制台的,有一个想法就是想让它打印到日志里,该如何做呢? 见下面的mybatis配置文件: <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-

  • 使用keras做SQL注入攻击的判断(实例讲解)

    本文是通过深度学习框架keras来做SQL注入特征识别, 不过虽然用了keras,但是大部分还是普通的神经网络,只是外加了一些规则化.dropout层(随着深度学习出现的层). 基本思路就是喂入一堆数据(INT型).通过神经网络计算(正向.反向).SOFTMAX多分类概率计算得出各个类的概率,注意:这里只要2个类别:0-正常的文本:1-包含SQL注入的文本 文件分割上,做成了4个python文件: util类,用来将char转换成int(NN要的都是数字类型的,其他任何类型都要转换成int/fl

  • 调试Django时打印SQL语句的日志代码实例

    这篇文章主要介绍了调试Django时打印SQL语句的日志代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 设置里面添加如下代码: LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': {

  • mybatis打印SQL,并显示参数的实例

    在maven的pom.xml里面添加一下依赖: <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>3.2.4.RELEASE</spring.version> <version.slf4j>1.7.6</version.slf4j> <version.logback>1.

  • java中用String.Join美化代码的实例讲解

    我们在java中处理字符串的时候,一般会选择String,在python中同样也是作用于字符串.那么我们今天延伸一下它的用法,只使用String作用于代码,会发生什么样的神奇效果呢?接下来我们使用String.Join对代码进行美化,下面一起看看怎么操作吧. 1.jadk1.8为我们提供了String.join()方法 2.几个使用的例子. 让我们在项目灵活的使用它,使代码更加优美 package com.niu.demo; import java.util.ArrayList; import

  • laravel join关联查询代码实例

    laravel join关联查询 1.两表关联 $fbaInventoryTb = (new \App\Model\Amz\Fba\InventoryReport)->getTable(); $productTb = (new \App\Model\Amz\Product)->getTable(); $twInventoryTb = (new \App\Model\TWUsa\TwusaInventory)->getTable(); $qry = \DB::table($fbaInven

随机推荐