Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 529|回复: 0

[默认分类] SQL Join的一些总结

[复制链接]
  • TA的每日心情
    开心
    2021-12-13 21:45
  • 签到天数: 15 天

    [LV.4]偶尔看看III

    发表于 2018-7-7 17:38:28 | 显示全部楼层 |阅读模式
    1.1.1 摘要
    Join是关系型数据库系统的重要操作之一,SQL Server中包含的常用Join:内联接、外联接和交叉联接等。如果我们想在两个或以上的表获取其中从一个表中的行与另一个表中的行匹配的数据,这时我们应该考虑使用Join,因为Join具体联接表或函数进行查询的特性
    本文将通过具体例子介绍SQL中的各种常用Join的特性和使用场合:
    目录

    Inner join  
    Outer join
    Cross join
    Cross apply
    Cross apply 和 Inner join的区别  
    Semi-join和Anti-semi-join  

    1.1.2 正文
    首先我们在tempdb中分别定义三个表College、Student和Apply,具体SQL代码如下:
    1. USE tempdb
    2. ---- If database exists the same name datatable deletes it.
    3. IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = "College") DROP TABLE College;
    4. IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = "Student") DROP TABLE Student;
    5. IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = "Apply") DROP TABLE Apply;
    6. ---- Create Database.
    7. create table College(cName nvarchar(50), state text, enrollment int);
    8. create table Student(sID int, sName nvarchar(50), GPA real, sizeHS int);
    9. 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代码如下:
    1. ---- Gets college information from college table
    2. ---- bases on college name.
    3. SELECT DISTINCT College.cName, College.enrollment
    4. FROM  College INNER JOIN
    5.         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代码如下:
    1. ---- Gets all college information
    2. SELECT College.cName, College.state, College.enrollment,
    3. Apply.cName, Apply.major, Apply.decision
    4. FROM  College LEFT OUTER JOIN
    5.         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)。使用完整外部联接,我们可以查询所有的学校,不管它们是否匹配联接谓词:
    1. ---- Gets all information from college and apply table.
    2. SELECT College.cName, College.state, College.enrollment,
    3. Apply.cName, Apply.major, Apply.decision
    4. FROM  College FULL OUTER JOIN
    5.         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代码如下:
    1. ---- College Cross join Apply.
    2. SELECT College.cName, College.state, College.enrollment,
    3. Apply.cName, Apply.major, Apply.decision
    4. FROM College
    5. 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进行查询:
    1. ---- Creates a function to get data from Apply base on sID.
    2. CREATE FUNCTION dbo.fn_Apply(@sID int)
    3. RETURNS @Apply TABLE (cName nvarchar(50), major nvarchar(50))
    4. AS
    5. BEGIN
    6.   INSERT @Apply SELECT cName, major FROM Apply where [sID] = @sID
    7.   RETURN   
    8. END
    9. ---- Student cross apply function fn_Apply.
    10. SELECT Student.sName, Student.GPA, Student.sizeHS,
    11. cName, major
    12. FROM Student CROSS APPLY dbo.fn_Apply([sID])
    复制代码
    我们也可以使用内部联接实现和Cross apply相同的查询功能,具体SQL代码如下:
    1. ---- Student INNER JOIN Apply bases on sID.
    2. SELECT Student.sName, Student.GPA, Student.sizeHS,
    3. cName, major
    4. FROM Student INNER JOIN [Apply]
    5. 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表示。
    1. ---- Student outer apply function fn_Apply.
    2. SELECT Student.sName, Student.GPA, Student.sizeHS,
    3. cName, major
    4. FROM Student OUTER APPLY dbo.fn_Apply([sID])
    复制代码

    图7 Outer apply查询
    Inner Join和Cross apply的区别
    首先我们知道Inner join是表和表的联接查询,而Cross apply是表和表值函数的联接查询,在前面Cross apply例子中,我们也可以通过Inner join实现相同的查询。
    1. ---- Student cross apply function fn_Apply.
    2. SET STATISTICS PROFILE ON
    3. SET STATISTICS TIME ON
    4. SELECT Student.sName, Student.GPA, Student.sizeHS,
    5. cName, major
    6. FROM Student CROSS APPLY dbo.fn_Apply([sID])
    7. SET STATISTICS PROFILE OFF
    8. SET STATISTICS TIME OFF
    复制代码
    1. ---- Student INNER JOIN Apply base on sID.
    2. SET STATISTICS PROFILE ON
    3. SET STATISTICS TIME ON
    4. SELECT Student.sName, Student.GPA, Student.sizeHS,
    5. cName, major
    6. FROM Student INNER JOIN [Apply]
    7. ON Student.sID = [Apply].sID
    8. SET STATISTICS PROFILE OFF
    9. 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代码如下:
    1. ---- Student Semi-join Apply base on sID.
    2. SELECT Student.sName, Student.GPA, Student.sizeHS
    3. ----[Apply].cName, [Apply].major
    4. FROM Student
    5. WHERE exists (
    6.     SELECT *
    7.     from [Apply]
    8.     where [Apply].sID = Student.sID
    9. )
    复制代码

    我们发现常用的EXISTS子句,原来是通过Left Semi Join实现的,所以说Semi-join在SQL Server中又许多使用场合。

    图9 查询结果

    图10 执行计划
    现在要求我们找出还没有申请学校的学生信息,这时我们立刻反应可以使用NOT EXISTS子句来实现该查询,具体SQL代码如下:
    1. ---- Gets student still not apply for school.
    2. SELECT Student.sID, Student.sName, Student.GPA, Student.sizeHS
    3. ----[Apply].cName, [Apply].major
    4. FROM Student
    5. WHERE NOT EXISTS (
    6.     SELECT *
    7.     FROM [Apply]
    8.     WHERE [Apply].sID = Student.sID
    9. )
    复制代码
    其实,我们常用的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的使用场合和特性。
    系列博文导航

    Deadlock的一些总结  
    SQL Transcation的一些总结

    参考

    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代码

    http://files.cnblogs.com/rush/Sample.rar
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-24 01:26 , Processed in 0.374128 second(s), 46 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表