今天我将介绍在SQLServer 中的三种连接操作符类型,分别是:循环嵌套、哈希匹配和合并连接。主要对这三种连接的不同、复杂度用范例的形式一一介绍。

  本文中使用了示例数据库AdventureWorks ,下面是下载地址:http://msftdbprodsamples.codeplex.com/releases/view/4004

简介:什么是连接操作符

  连接操作符是一种算法类型,它是SQLServer优化器为了实现两个数据集合之间的逻辑连接选择的操作符。优化器可以基于请求查询、可用索引、统计信息和估计行数等不同的场景为每一套数据选择不同的算法

  通过查看执行计划可以发现操作符如何被使用。接下来我们看一下如何具体使用。

NESTED LOOPS(循环嵌套)

  我们通过下面的例子来展示一下(查询2001年7月份的数据):

SELECT
OH.OrderDate, OD.OrderQty, OD.ProductID, OD.UnitPrice
FROM
Sales.SalesOrderHeader AS OH
JOIN
Sales.SalesOrderDetail AS OD
ON
OH.SalesOrderID = OD.SalesOrderID
WHERE
OH.OrderDate BETWEEN '2001-07-01' AND '2001-07-31'

执行计划的结果如下:

图右上方的叫“outer input”,在其下面的叫做“inner input

本质上讲,“Nested Loops”操作符就是:为每一个记录的外部输入找到内部输入的匹配行。

技术上讲,这意味着外表聚集索引被扫描获取外部输入相关的记录,然后内表聚集索引查找每一个匹配外表索引的记录。

我们可以通过把鼠标放在聚集索引扫描操作符上面来验证这个信息,请看这个tooltip:

看这个执行的估计行数是1,索引查找tooltip如下:

这次发现执行的估计行数是179,这是很接近返回的外部输入行的。

按照复杂度计算(假设N是外部输出的行数,M是总行数在SalesOrderDetai表的):查询复杂度是O(NlogM),这里logM是在内部输入表的每次查找的复杂度。

当外部输入比较小并且内部输入有索引在连接的字段上的时候SQLServer 优化器更喜欢选择这种操作符类型(Nested Loop)。外部和内部输入的数据行差距越大,这个操作符提供的性能越高。

MERGE Join(合并连接)

“Merge”算法是连接两个较大且按序存储的在连接键上最有效的方式。请看一下下面这个查询例子(查询返回用户和销售表的ID):

SELECT
OC.CustomerID, OH.SalesOrderID
FROM
Sales.SalesOrderHeader AS OH
JOIN
Sales.Customer AS OC
ON
OH.CustomerID = OC.CustomerID

查询执行计划如下:

  • 首先我们注意到两套数据是在CustomerID上是有序的:因为聚集索引是有序的且在SalesorderHeader表上该字段是非聚集索引。
  • 根据在操作符的箭头(鼠标放在上面),我们能看到每个返回结果行数都是很大的。
  • 除此之外,在On 的子句后面要用=操作符。

就是这三个因素会导致优化器选择Merge Join查询操作符。

使用这种连接操作符的最大的性能就是两个输入操作符执行一次。我们能把鼠标放在两个数据的上面看一下执行的次数都是1,也就是说算法是很有效率的。

合并连接同时读取两个输入然后比较他们。如果匹配就返回,否则,行数较小的被放弃,因为两边输入是有序的。放弃的行不再匹配任何行。

知道其中一个表完毕一直重复匹配,即使另一个表还有数据,那么最大的时间复杂的消耗就是两个表完全不同键值,那么最大的复杂度就是: O(N+M)。

HASH Match(哈希匹配)

“Hash”连接是我们称为 “the go-to guy” 的操作符。当其他连接操作符都不支持的场景时,就会选择这种操作符。比如当表恰好不排序,或者没有索引时。当优化器选择这种操作符,一般来说可能是我们没有做好一些基础工作(例如,加索引)。但是有些情况(复杂查询)没有别的方式,只能选择它。

请看下面这个查询(获取contacts 表中姓和名中以“John”开始的包含销售的ID字段的数据集):

SELECT
OC.FirstName, OC.LastName, OH.SalesOrderID
FROM
Sales.SalesOrderHeader AS OH
JOIN
Person.Contact AS OC
ON
OH.ContactID = OC.ContactID
WHERE
OC.FirstName LIKE 'John%'

The execution plan looks like this:

由于ContactID列没有索引,所以选择哈希操作符。

在深入理解这个例子之前,介绍两个重要的概念:一个是“Hashing”函数,一个是“Hash Table”。

函数是一个程序性函数,它接收1或者多个值然后转换他们为一个符号值(通常是数字)。这个函数通常是单向的,意味着不能反转回来原始值,但是确定性保证如果你提供了相同的值,符号值是完全一样的。也就是说,几个不同的输入值,可以有相同的Hash值。

“Hash Table”是一个数据结构,把所有行都放到一个相同尺寸的桶里面。每一个桶代表一个哈希值。这意味着当你激活函数的行,使用结果你就会知道它属于哪个桶。

利用统计信息,SQLServer 会选择较小的两个数据输入来提供构造输入,并且输入被用来在内存中创建哈希表。如果没有足够的内存,在tempdb中会使用物理磁盘。在哈希表建立后,SQLServer将从较大的表中得到数据,叫做探测输入。利用哈希匹配函数与哈希表比较,然后返回匹配行。在图形执行计划中,构造输入的查询在上面,探测输入的查询在下面。

只要较小的表非常小,这个算法就是非常有效的。但是如果两个表都非常大,这可能是非常低效的执行计划。

查询Hints

利用Hints,破事SQLServer使用指定的连接类型。但是强烈不推荐这么做,尤其在生产环境,因为没有永恒的最佳选择(因为数据在变化),并且优化器通常是正确的。

添加OPTION 子句作为查询的结尾,使用关键字LOOP JOINMERGE JOIN 或者 HASH JOIN可以强制执行连接。

看看如何实现:

SELECT OC.CustomerID, OH.SalesOrderID
FROM Sales.SalesOrderHeader AS OH
JOIN Sales.Customer AS OC
ON OH.CustomerID = OC.CustomerID
OPTION (HASH JOIN) SELECT OC.FirstName, OC.LastName, OH.SalesOrderID
FROM Sales.SalesOrderHeader AS OH
JOIN Person.Contact AS OC
ON OH.ContactID = OC.ContactID
WHERE OC.FirstName LIKE 'John%'
OPTION (LOOP JOIN) SELECT OC.FirstName, OC.LastName, OH.SalesOrderID
FROM Sales.SalesOrderHeader AS OH
JOIN Person.Contact AS OC
ON OH.ContactID = OC.ContactID
WHERE OC.FirstName LIKE 'John%'
OPTION (MERGE JOIN)

总结

Nested Loops

  • 复杂度: O(NlogM)。
  • 其中一个表很小的时候。
  • 较大的表允许使用索引查找连接字段。

Merge Join

  • 复杂度: O(N+M)。
  • 两个输入的连接字段是有序的。
  • 使用=操作符
  • 适合非常大的表

Hash Match

  • 复杂度: O(N*hc+M*hm+J)
  • 最后默认的操作符
  • 使用哈希表和动态哈希匹配函数匹配行

本篇随笔详细介绍了三种链接操作符和它们的触发机制,当然这些也都是动态的,就像前面说的没有最佳的操作符,只有最合适的,要根据实际请款选择不同的操作符。

SQL连接操作符介绍(循环嵌套, 哈希匹配和合并连接)的更多相关文章

  1. 关于SQL while 循环嵌套 外部循环数据无法进入内部循环

    下面一般是,作为SQL新手第一次写循环嵌套的办法,但是大家会发现一个问题,那就是变量@i总是不能进入第二个循环. declare @i int ,@j int, @k int set @j = 1 - ...

  2. oracle表连接------>排序合并连接(Merge Sort Join)

    排序合并连接 (Sort Merge Join)是一种两个表在做连接时用排序操作(Sort)和合并操作(Merge)来得到连接结果集的连接方法. 对于排序合并连接的优缺点及适用场景例如以下: a,通常 ...

  3. SQL的循环嵌套算法:NLP算法和BNLP算法

    MySQL的JOIN(二):JOIN原理 表连接算法 Nested Loop Join(NLJ)算法: 首先介绍一种基础算法:NLJ,嵌套循环算法.循环外层是驱动表,循坏内层是被驱动表.驱动表会驱动被 ...

  4. [转载]2.6 UiPath循环嵌套的介绍和使用

    一.循环嵌套的介绍 一个循环体内又包含另一个完整的循环结构,就称之为循环嵌套.内嵌的循环中还可以嵌套循环,这就是多层循环,也叫做多重循环. 二.在UiPath中结合使用循环嵌套生成99乘法表 1.打开 ...

  5. java基础:进制详细介绍,进制快速转换,二维数组详解,循环嵌套应用,杨辉三角实现正倒直角正倒等腰三角,附练习案列

    1.Debug模式 1.1 什么是Debug模式 是供程序员使用的程序调试工具,它可以用于查看程序的执行流程,也可以用于追踪程序执行过程来调试程序. 1.2 Debug介绍与操作流程 如何加断点 选择 ...

  6. c#循环语句 for 循环嵌套的练习。还有跳转语句,异常语句,迭代穷举介绍

    先说一下循环嵌套:循环嵌套就是再一个循环里面再放一个循环,也就是说如果没一个循环都循环10次,那么第一个循环是1的时候,嵌套的循环会循环十次.也就是10*10的效果. for 循环语句 主要还是逻辑思 ...

  7. C#用链式方法表达循环嵌套

    情节故事得有情节,不喜欢情节的朋友可看第1版代码,然后直接跳至“三.想要链式写法” 一.起缘 故事缘于一位朋友的一道题: 朋友四人玩LOL游戏.第一局,分别选择位置:中单,上单,ADC,辅助:第二局新 ...

  8. 关系数据库SQL之高级数据查询:去重复、组合查询、连接查询、虚拟表

    前言 接上一篇关系数据库SQL之基本数据查询:子查询.分组查询.模糊查询,主要是关系型数据库基本数据查询.包括子查询.分组查询.聚合函数查询.模糊查询,本文是介绍一下关系型数据库几种高级数据查询SQL ...

  9. DBCP连接池介绍

    DBCP连接池介绍 ----------------------------- 目前 DBCP 有两个版本分别是 1.3 和 1.4. DBCP 1.3 版本需要运行于 JDK 1.4-1.5 ,支持 ...

随机推荐

  1. HTML5网页录音和压缩,边猜边做..(附源码)

    宣传一下自己的qq群: (暗号:C#交流) 欢迎喜欢C#,热爱C#,正在学习C#,准备学习C#的朋友来这里互相学习交流,共同进步 群刚建,人不多,但是都是真正热爱C#的 我也是热爱C#的 希望大家可以 ...

  2. ABP框架搭建项目系列教程基础版完结篇

    返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 经过前面十二篇的基础教程,现在终于该做个总结了. 回顾 第一篇,我们建议新手朋友们先通过ABP官网的启动模板生成解决方案,因为这样 ...

  3. Entity Framework 6 Recipes 2nd Edition(13-5)译 -> 使POCO的修改追踪更高

    问题 你正在使用POCO,你想提高修改跟踪的性能,同时使内存消耗更少.另外,你想通过EF的CodeFirst方式来实现. 解决方案 假设你有一个关于Account(帐户)和相关的Payments(支付 ...

  4. JSP页面跳转的几种实现方法

    使用href超链接标记      客户端跳转 使用JavaScript               客户端跳转 提交表单                        客户端跳转 使用response ...

  5. 【Win 10 应用开发】在后台进行多媒体转码

    前面,老周给大伙儿讲了如何运用 MediaTranscoder 类来完成多媒体.然而,你懂的,要是多媒体文件比较大,转码时间会更长,有可能用户不会一眭停在当前应用界面上,或许会切换到其他应用程序,甚至 ...

  6. angular2系列教程(五)Structural directives、再谈组件生命周期

    今天,我们要讲的是structural directives和组件生命周期这两个知识点.structural directives顾名思义就是改变dom结构的指令.著名的内建结构指令有 ngIf, n ...

  7. C# 复制PDF页面到另一个PDF文档

    C# 复制PDF页面到另一个PDF文档 有时候我们可能有这样一个需求,那就是把PDF页面从一个PDF文档复制到另一个PDF文档中.由于PDF文档并不像word文档那样好编辑,因此复制也相对没有那么容易 ...

  8. c 网络与套接字socket

    我们已经知道如何使用I/O与文件通信,还知道了如何让同一计算机上的两个进程进行通信,这篇文章将创建具有服务器和客户端功能的程序 互联网中大部分的底层网络代码都是用C语言写的. 网络程序通常有两部分组成 ...

  9. Python爬虫小白入门(三)BeautifulSoup库

    # 一.前言 *** 上一篇演示了如何使用requests模块向网站发送http请求,获取到网页的HTML数据.这篇来演示如何使用BeautifulSoup模块来从HTML文本中提取我们想要的数据. ...

  10. Nginx与tomcat组合的简单使用

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 项目中瓦片资源越来越多,如果提高瓦片的访问效率是一个需要解决的 ...