SQL链接操做符介绍(循环嵌套, 哈希匹配和合并链接)

 

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

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

简介:什么是链接操做符

  链接操做符是一种算法类型,它是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'

 

执行计划的结果以下:oop

join types 1 exec plan

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

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

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

咱们能够经过把鼠标放在汇集索引扫描操做符上面来验证这个信息,请看这个tooltip:code

join types 2 details

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

join types 3 details

此次发现执行的估计行数是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

 

查询执行计划以下:

join types 4 exec plan

  • 首先咱们注意到两套数据是在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:

join types 5 exec plan

因为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)
  • 最后默认的操做符
  • 使用哈希表和动态哈希匹配函数匹配行

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

相关文章
相关标签/搜索