自测试示例, 两表 都没有 建立索引 情况:  start

1.  sql 语句通过 nested loop 连接,

2. 默认 hash join

//end-------------------------------------------------------------------

说明:最近找到了一个不错的国外的博客http://blogs.msdn.com/b/craigfr/,博主是Sql Server的开发人员,写了很多Sql Server的内部原理性的文章,觉得非常有收获。所以试着把他翻译成中文,因为本人的英语和技术水平有限,难免会有错误,还请各位看官批评指教。

Nested Loops Join(嵌套连接)

Sql Server支持三种物理连接:nested loops join,merge join和hash join.这篇文章,我将描述nested loops join
(或者简称为NL)。

基本算法

最简单的情况是,nested loop会以连接谓词为条件取出一张表里的每一行(称为外部表)
与另外一张表(称为内部表)的每一行进行比较来寻找符合条件的行。(注意这里的"内部"和"外部"是具有多层含义的,必须从
上下文中来理解它们。"内部表"和"外部表"是指连接的输入,"内连接"和"外连接"是指逻辑操作。)

我们可以用伪码来解释这个算法:

for each row R1 in the outer table
    for each row R2 in the inner table
        if R1 joins with R2
            return (R1, R2)

因为算法里的嵌套循环,所以命名为嵌套连接。

从比较的总行说来说,这种算法的成本是与外部表行数乘以内部表的行数成比例的。随着驱动表行数的增长
的成本增长是很快的,在实际情况我们通过减少内部表行数来减小算法的成本的。

还是以上篇文章给出的方案为例:create table Customers (Cust_Id int, Cust_Name varchar(10))
insert Customers values (1, 'Craig')
insert Customers values (2, 'John Doe')
insert Customers values (3, 'Jane Doe')create table Sales (Cust_Id int, Item varchar(10))
insert Sales values (2, 'Camera')
insert Sales values (3, 'Computer')
insert Sales values (3, 'Monitor')
insert Sales values (4, 'Printer')进行如下查询:select *
from Sales S inner join Customers C
on S.Cust_Id = C.Cust_Id
option(loop join)我加入了"loop join"提示来强迫优化器使用nested loops join.和"set statistics profile on"
一起运行得到如下的执行计划:Rows Executes3    1         |--Nested Loops(Inner Join, WHERE:([C].[Cust_Id]=[S].[Cust_Id]))3    1            |--Table Scan(OBJECT:([Customers] AS [C]))12   3            |--Table Scan(OBJECT:([Sales] AS [S]))

这份执行计划里Customers是外部表,Sales是内部表。首先扫描Customers表。每次取出一个Customer,
对于每一个customer,都要扫描Sales表。因为有3个Customers,所以Sales表被扫描了3次。每次扫描返回
4行。判断每一个sale与当前的customer是否具有相同的Cust_Id,如果相同就返回这一对行.我们有3个
customer和4个sale所以我们进行了3*4=12次比较。其中只有3次比较符合条件。

如果在Sales表创建索引会是什么情况呢:create clustered index CI on Sales(Cust_Id)我们得到了如下的执行计划:Rows Executes3    1        |--Nested Loops(Inner Join, OUTER REFERENCES:([C].[Cust_Id]))3    1           |--Table Scan(OBJECT:([Customers] AS [C]))3    3           |--Clustered Index Seek(OBJECT:([Sales].[CI] AS [S]), SEEK:([S].[Cust_Id]=[C].[Cust_Id]) ORDERED FORWARD)

这次,并没有做全表扫描,而是进行了索引探寻。仍然进行了3次索引探寻-每个customer一次,
但是每次索引探寻只返回了与当前Cust-Id相匹配并满足谓词条件的一条记录。所以,索引探寻只返回了
3行,而不是全表扫描的12行。

请注意这里索引探寻的依赖条件C.CustId来自于连接的外部表-Customers全表扫描。
每次我们执行索引探寻(再次说明我们执行了3次-每个用户一次),C_CustId有不同的值。
我们称C.CustId为"关联参数";如果一个nested loops join有关联参数,执行计划里会以"OUTER REFERENCES"
显示出来。我们经常把这种以依赖于关联参数的索引探寻方式执行的nested loop join称为
"索引连接"。这是非常常见的场景。

  

Nested loops join支持什么类型的连接谓词?

Nested loops join支持包括相等连接谓词和不等谓词连接在内的所有连接谓词。

Nested loops join支持什么类型的逻辑连接?

Nested loops join支持以下类型的逻辑连接:

* Inner join
* Left outer join
* Cross join
* Cross apply and outer apply
* Left semi-join and left anti-semi-join

Nested loops join不支持以下逻辑连接:

* Right and full outer join
* Right semi-join and right anti-semi-join

为什么Nested loops join 只支持左连接?

我们很容易扩展Nested loops join 算法来支持left outer 和semi-joins.例如,下边是左外连接的伪码。
我们可以写出相似的代码来实现 left semi-join 和 left anti-semi-join.

for each row R1 in the outer table
    begin
        for each row R2 in the inner table
            if R1 joins with R2
                return (R1, R2)
        if R1 did not join
            return (R1, NULL)
    end

这个算法记录我们是否连接了一个特定的外部行。如果已经穷尽了所有内部行,但是没有找到一个
符合条件的内部行,就把该外部行做为NULL扩展行输出。

那么我们为什么不支持right outer join呢。在这里,我们想返回符合条件的行对(R1,R2)
和不符合连接条件的(NULL,R2)。问题是我们会多次扫描内部表-对于外部表的每行都要扫描一次。
在多次扫描过程中我们可能会多次处理内部表的同一行。这样我们就无法来判断某一行到底符合
不符合连接条件。更进一步,如果我们使用index join,一些内部行可能都不会被处理,但是这些行在
外连接时是应该返回的。

幸运的是right outer join可以转换为left outer join,right semi-join可以转换为left semi-join,
所以right outer join和semi-joins是可以使用nested loops join的。但是,当执行转换的时候可能会
影响性能。例如,上边方案中的"Customer left outer join Sales",由于表内部表Sales有聚集索引,所以
我们在连接过程中可以使用索引探寻。如果"Customer right outer join Sales" 转换为 "Sales left outer 
join Customer”,我们则需要在Customer表上具有相应的索引了。

full outer joins是什么情况呢?

nested loops join完全支持outer join.我们可以把"T1 full outer join T2"转换为"T1 left outer join T2 
UNION T2 left anti-semi-join T1".可以这样来理解,将full outer join转换为一个左连接-包含T1和T2所有的
符合条件的连接行和T1表里没有连接的行,然后加上那些使用anti-semi-join从T2返回的行。下边是转换过程:

select *
from Customers C full outer join Sales S
on C.Cust_Id = S.Cust_Id

Rows Executes
  
 
5    1        |--Concatenation
 
4    1           |--Nested Loops(Left Outer Join, WHERE:([C].[Cust_Id]=[S].[Cust_Id]))
 
3    1           |    |--Table Scan(OBJECT:([Customers] AS [C]))
 
12   3           |    |--Clustered Index Scan(OBJECT:([Sales].[Sales_ci] AS [S]))
 
0    0           |--Compute Scalar(DEFINE:([C].[Cust_Id]=NULL, [C].[Cust_Name]=NULL))
 
1    1                |--Nested Loops(Left Anti Semi Join, OUTER REFERENCES:([S].[Cust_Id]))
 
4    1                  |--Clustered Index Scan(OBJECT:([Sales].[Sales_ci] AS [S]))
 
3    4                  |--Top(TOP EXPRESSION:((1)))
 
3    4                       |--Table Scan(OBJECT:([Customers] AS [C]), WHERE:([C].[Cust_Id]=[S].[Cust_Id]))

注意:在上边的例子中,优化器并选择了聚集索引扫描而不是探寻。这完全是基于成本考虑而做出的决定。表非常小(只有一页)
所以扫描或探寻并没有什么性能上的区别。

NL join好还是坏?

实际上,并没有所谓"最好"的算法,连接算法也没有好坏之分。每一种连接方式在正确的环境下性能非常好,
而在错误的环境下则非常差。因为nested loops join的复杂度是与驱动表大小和内部表大小乘积成比例的,所以在驱动表比较小
的情况下性能比较好。内部表不需要很小,但是如果非常大的话,在具有高选择性的连接列上建立索引将很有帮助。

一些情况下,Sql Server只能使用nested loops join算法,比如Cross join和一些复杂的cross applies,outer applies,
(full outer join是一个例外)。如果没有任何相等连接谓词的话nested loops join算法是Sql Server的唯一选择。

参考 :

https://blog.csdn.net/wangyihbu/article/details/6209294

https://blog.csdn.net/oxlshmily/article/details/8563785

转载于:https://www.cnblogs.com/xiaohuizhenyoucai/p/11010088.html

SqlServer 算法 :Nested Loops Join(嵌套连接)相关推荐

  1. 嵌套循环连接(Nested Loops), 合并联接(Merge), 哈希联接(Hash)的适用情况

    1.嵌套循环连接(Nested Loops)适用范围 两个表, 一个叫外部表, 一个叫内部表. 如果外部输入非常小,而内部输入非常大并且已预先建立索引,那么嵌套循环联接将特别有效率. 关于连接时哪个表 ...

  2. oracle join 嵌套,誰能真正理解hash join/nested loop/merge join

    关于这三种JOIN的理解.Nested loop join,Hash join,Sort merge join Nested loop join: 步骤:确定一个驱动表(outer table),另一 ...

  3. Oracle 表的连接方式(1)-----Nested loop join和 Sort merge join

    关系数据库技术的精髓就是通过关系表进行规范化的数据存储,并通过各种表连接技术和各种类型的索引技术来进行信息的检索和处理. 表的三种关联方式: nested loop:从A表抽一条记录,遍历B表查找匹配 ...

  4. clickhouse 复杂查询时嵌套连接join可能存在的异常解决(xjl456852原创)

    clickhouse 复杂查询时嵌套连接join可能存在的异常解决(xjl456852原创) 参考文章: (1)clickhouse 复杂查询时嵌套连接join可能存在的异常解决(xjl456852原 ...

  5. SQL数据库语言基础之SqlServer多表连接查询与INNER JOIN内连接查询

    文章目录 一.简单连接查询 二.多表连接查询 三.INNER JOIN 内连接查询 一.简单连接查询 1.直接连接:无连接规则连接两表,得到的是两个表的笛卡尔积. 连接后的行数=表1行数*表2行数 连 ...

  6. oracle hash join outer,CSS_浅谈Oracle中的三种Join方法,基本概念 Nested loop join: Outer - phpStudy...

    浅谈Oracle中的三种Join方法 基本概念 Nested loop join: Outer table中的每一行与inner table中的相应记录join,类似一个嵌套的循环. Sort mer ...

  7. Nested Loop Join入门

    Nested Loop Join 之间的表关联是使用索引进行匹配的,假设表 R 和 S 进行连接,其算法伪代码大致如下: for each row r in R with matching condi ...

  8. mysql的join算法_mysql的Join算法-阿里云开发者社区

    实为吾之愚见,望诸君酌之!闻过则喜,与君共勉 测试数据 CREATE TABLE `dept_emp` ( `emp_no` int(11) NOT NULL, `dept_no` char(4) N ...

  9. oracle 嵌套 哈希,Oracle-三种联接方法(哈希连接、嵌套连接、笛卡儿乘积)

    在数据库系统中执行一个查询SQL语句,如果这个查询只操作一张表,那么仅仅涉及到这个表及关联对象的访问. 访问方式通常是三种:全表扫描.全索引扫描和索引扫描. 如果这个查询操作两张及以上的表,那么需要操 ...

最新文章

  1. 关于SAP BW提示“Carry out repairs in non-original only
  2. matlab欧拉迭代,matlab机械臂正逆运动学求解问题,使用牛顿-欧拉迭代算法
  3. 数据结构 归并排序 C++
  4. java jvm调优面试题_【Java面试题第一期】有没有jvm调优经验?调优方案有哪些?...
  5. 加密服务器显示到期,注册加密卡成功了,打开软件还是提示演示到期了,怎么解决?...
  6. ajax实现上传文件
  7. c语言程序前言,C语言 程序代码编写规范前言
  8. mysql 交叉统计_Mysql静态行列转换交叉查询
  9. 随便玩玩系列之一:SPOJ-RNG+51nod 算法马拉松17F+51nod 1034 骨牌覆盖v3
  10. 快播王欣再做视频;Apple Watch 非法雇佣学生;ofo 进军电单车 | 极客头条
  11. WiFi----Wireshark抓包及分析说明
  12. 如何制作一个横版格斗过关游戏_AppStore限免推荐丨加油打工人 竞速+横版格斗2款游戏陪你过周末?_手机游戏...
  13. 如何像“二次元萌妹”小冰一样写诗创作和即兴编曲?
  14. ccf csp 题目:门禁系统
  15. Mac 常用的 20 款效率神器推荐
  16. 续费Enom域名的三种办法
  17. #define 喵 int_发现这个领养日,狗子觉得不能让喵独占鳌头
  18. 我的世界斗罗封神服务器怎么注册,我的世界斗罗封神服务器-我的世界斗罗封神mod手机版v1.17.2.01-游戏宝手游网...
  19. 创维30周年庆典举行,中国制造业标杆向千亿目标加速冲刺
  20. 使用OAuth2的SSO分析

热门文章

  1. 5.Vue 计算属性和侦听器
  2. 这年头学爬虫还就得会点 scrapy 框架
  3. 11.2.3 事件
  4. 【STM32】RTC相关函数和类型
  5. 【IT资讯】TIOBE - 2020年7月编程语言排行
  6. 【Linux系统编程】进程间通信之无名管道
  7. 【Tools】C/C++开发SDK下载汇总
  8. stm32f103电子钟心得体会_浅谈STM32_RTC闹钟
  9. python中classmethod的用法_Python中的@classmethod是如何使用的?
  10. 1036 跟奥巴马一起编程 (15 分)(c++)C++