今天我将介绍在SQLServer 中的三种链接操做符类型,分别是:循环嵌套、哈希匹配和合并链接。主要对这三种链接的不一样、复杂度用范例的形式一一介绍。算法
本文中使用了示例数据库AdventureWorks ,下面是下载地址:http://msftdbprodsamples.codeplex.com/releases/view/4004sql
链接操做符是一种算法类型,它是SQLServer优化器为了实现两个数据集合之间的逻辑链接选择的操做符。优化器能够基于请求查询、可用索引、统计信息和估计行数等不一样的场景为每一套数据选择不一样的算法数据库
经过查看执行计划能够发现操做符如何被使用。接下来咱们看一下如何具体使用。数据结构
咱们经过下面的例子来展现一下(查询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
图右上方的叫“outer input”,在其下面的叫作“inner input” 性能
本质上讲,“Nested Loops”操做符就是:为每个记录的外部输入找到内部输入的匹配行。优化
技术上讲,这意味着外表汇集索引被扫描获取外部输入相关的记录,而后内表汇集索引查找每个匹配外表索引的记录。this
咱们能够经过把鼠标放在汇集索引扫描操做符上面来验证这个信息,请看这个tooltip:code
看这个执行的估计行数是1,索引查找tooltip以下:
此次发现执行的估计行数是179,这是很接近返回的外部输入行的。
按照复杂度计算(假设N是外部输出的行数,M是总行数在SalesOrderDetai表的):查询复杂度是O(NlogM),这里logM是在内部输入表的每次查找的复杂度。
当外部输入比较小而且内部输入有索引在链接的字段上的时候SQLServer 优化器更喜欢选择这种操做符类型(Nested Loop)。外部和内部输入的数据行差距越大,这个操做符提供的性能越高。
“Merge”算法是链接两个较大且按序存储的在链接键上最有效的方式。请看一下下面这个查询例子(查询返回用户和销售表的ID):
SELECT OC.CustomerID, OH.SalesOrderID FROM Sales.SalesOrderHeader AS OH JOIN Sales.Customer AS OC ON OH.CustomerID = OC.CustomerID
查询执行计划以下:
就是这三个因素会致使优化器选择Merge Join查询操做符。
使用这种链接操做符的最大的性能就是两个输入操做符执行一次。咱们能把鼠标放在两个数据的上面看一下执行的次数都是1,也就是说算法是颇有效率的。
合并链接同时读取两个输入而后比较他们。若是匹配就返回,不然,行数较小的被放弃,由于两边输入是有序的。放弃的行再也不匹配任何行。
知道其中一个表完毕一直重复匹配,即便另外一个表还有数据,那么最大的时间复杂的消耗就是两个表彻底不一样键值,那么最大的复杂度就是: O(N+M)。
“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,破事SQLServer使用指定的链接类型。可是强烈不推荐这么作,尤为在生产环境,由于没有永恒的最佳选择(由于数据在变化),而且优化器一般是正确的。
添加OPTION 子句做为查询的结尾,使用关键字LOOP JOIN, MERGE 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)
本篇随笔详细介绍了三种连接操做符和它们的触发机制,固然这些也都是动态的,就像前面说的没有最佳的操做符,只有最合适的,要根据实际请款选择不一样的操做符。