1 作用

在MySQL中join操作被称为连接,作用是能连接多个表的数据(通过连接条件),从多个表中获取数据合并在一起作为结果集返回给客户端。例如:

表A:

id

name

age

1

A

18

2

B

19

3

C

20

表B:

id

uid

gender

1

1

F

2

2

M

通过连接可以获取到合并两个表的数据:

select A.*,B.gender from A left join B on A.id=B.uid

id

name

age

gender

1

A

18

F

2

B

19

M

3

C

20

null

2 连接关键字

连接两个表我们可以用两个关键字:on,using。on可以指定具体条件,using则指定相同名字和数据类型的列作为等值判断的条件,多个则通过逗号隔开。

如下:

on: select * from A join B on A.id=B.id and B.name=''

using: select * from A join B using(id,name) = select * from A join B on A.id=B.id and A.name=B.name

3 连接类型

3.1 内连接

内连接和交叉连接

语法:A join | inner join | cross join B

表现:A和B满足连接条件记录的交集,如果没有连接条件,则是A和B的笛卡尔积

特点:在MySQL中,cross join ,inner join和join所实现的功能是一样的。因此在MySQL的官方文档中,指明了三者是等价的关系。

隐式连接

语法:from A,B,C

表现:相当于无法使用on和using的join

特点:逗号是隐式连接运算符。 隐式连接是SQL92中的标准内容,而在SQL99中显式连接才是标准,虽然很多人还在用隐私连接,但是它已经从标准中被移除。从使用的角度来说,还是推荐使用显示连接,这样可以更清楚的显示出多个表之间的连接关系和连接依赖的属性。

3.2 外连接

左外连接

语法:A left join B

表现:左表的数据全部保留,右表满足连接条件的记录展示,不满足的条件的记录则全是null

右外连接

语法:A right join B

表现:右表的数据全部保留,左表满足连接条件的记录展示,不满足的条件的记录则全是null

全外连接

MySQL不支持全外连接,只支持左外连接和右外连接。如果要获取全连接的数据,要可以通过合并左右外连接的数据获取到,如 select * from A left join B on A.name = B.name union select * from A right join B on B.name = B.name;。

这里union会自动去重,这样取到的就是全外连接的数据了。

3.3 自然连接

语法:A natural join B ==== A natural left join B ==== A natural right join B

表现:相当于不能指定连接条件的连接,MySQL会使用左右表内相同名字和类型的字段作为连接条件。

特点:自然连接也分自然内连接,左外连接,右外连接,其表现和上面提到的一致,只是连接条件由MySQL自动判定。

4 执行顺序

在连接过程中,MySQL各关键字执行的顺序如下:

from -> on|using -> where -> group by -> having -> select -> order by -> limit

可以看到,连接的条件是先于where的,也就是先连接获得结果集后,才对结果集进行where筛选。

5 连接算法

join有三种算法,分别是Nested Loop Join,Hash join,Sort Merge Join。MySQL官方文档中提到,MySQL只支持Nested Loop Join这一种算法。

具体来说Nested Loop Join又分三种细分的算法:

SNLJ

BNLJ

INLJ

我们来看下对于连接语句select * from A left join B on A.id=B.tid,这三种算法是怎么连接的。

5.1 Simple Nested Loop Join(SNLJ)

SNLJ是在没有使用到索引的情况下,通过两层循环全量扫描连接的两张表,得到符合条件的两条记录则输出。也就是让两张表做笛卡尔积进行扫描,是比较暴力的算法,会比较耗时。其过程如下:

for (a in A) {

for (b in B) {

if (a.id == b.tid) {

output ;

}

}

}

当然,MySQL即使在无索引可用,或者判断全表扫描可能比使用索引更快的情况下,还是不会选择使用过于粗暴的SNLJ算法,而是采用下面的算法。

5.2 Block Nested Loop Join(BNLJ)

INLJ是MySQL无法使用索引的时候采用的join算法。会将外层循环的行分片存入join buffer, 内层循环的每一行与整个buffer中的记录做比较,从而减少内层循环的次数,具体逻辑如下:

for (blockA in A.blocks) {

for (b in B) {

if (b.tid in blockA.id) {

output ;

}

}

}

相比于SNLJ算法,BNLJ算法通过外层循环的结果集的分块,可以有效的减少内层循环的次数。

原理

举例来说,外层循环的结果集是100行,使用SNLJ算法需要扫描内部表100次,如果使用BNLJ算法,假设每次分片的数量是10,则会先把对Outer Loop表(外部表)每次读取的10行记录放到join buffer,然后在InnerLoop表(内部表)中每次循环都直接匹配这10行数据,这样内层循环只需要10次,对内部表的扫描减少了9/10,所以BNLJ算法就能够显著减少内层循环表扫描的次数。

当然这里,不管SNLJ还是BNLJ算法,他们总的比较次数都是一样的,都是要拿外层循环的每一行与内层循环的每一行进行比较。

BNLJ算法减少的是总的扫描行数,SNLJ算法是外层循环要一行行扫描A表的数据,然后取A.id去表B一行行扫描看是否匹配。而BNLJ算法则是外层循环要一行行扫描A表的数据,然后放到内存分块里,然后去表B一行行扫描,扫描出来的B的一行数据与内存分块里的A的数据块进行比较。这里可以一次就是很多行A的数据与B的数据进行比较,而且是在内存中进行比较,速度更加快了。

影响因素

这里BNLJ算法总的扫描行数是由外层循环的数据量N,和分块数量K还有内层循环的数据量M决定的。其中分块数量K与外层循环的数据量N又是息息相关的,我们可以表示为λN,其中λ取值为(0~1)。则总扫描次数C=N+λNM。

可以看出,在这个式子里,N和λ的大小都会影响扫描行数,但是λ才是影响扫描行数的关键因素,这个值越小越好(除非N和M的差值非常大,这时候N才会成为关键影响因素)。

那什么会影响 λ 的大小呢?那就是 MySQL的join_buffer_size设置项的大小了。λ和join_buffer_size成倒数关系,join_buffer_size越大,分块越大,λ越小,分块数量也就越少,也就是外层循环的次数也越少。所以在使用不上索引的时候,我们要优先考虑扩大join_buffer_size的大小,这样优化效果会更明显。而在能使用上索引的时候,MySQL会使用以下算法来进行join。

5.3 Index Nested Loop Join(INLJ)

INLJ是MySQL判断能使用到被驱动表的索引的情况下采用的算法。假设A表的数据行为10,B表的数据行为100,且B.tid建立了索引,则对于select * from A left join B on A.id=B.tid,MySQL会采用Index Nested Loop Join。其过程如下:

for (a in A) {

if (a.id in B.tid.Index) {

output ;

}

}

总共需要循环10次A,每次循环的时候通过索引查询一次B的数据。而如果我们反过来是B left join A的话,总共要循环100次B,由此可见如果使用join的话,需要让小表做驱动表,这样才能有效减少循环次数。但是需要注意的是,这个结论的前提是可以使用被驱动表的索引。

INLJ内层循环读取的是索引,可以减少内存循环的次数,提高join效率,但是也有缺点的,就是如果扫描的索引是非聚簇索引,并且需要访问非索引的数据,会产生一个回表读取数据的操作,这就多了一次随机的I/O操作。例如上面在索引里匹配到了tid,还要去找tid所在的行在磁盘所在的位置,具体可以见我以前的文章:MySQL索引详解之索引的存储方式。

6 注意点

尽量增加连接条件,减少join后数据集的大小

用小结果集驱动大结果集,将筛选结果小的表首先连接,再去连接结果集比较大的表

被驱动表的被join的字段要建立索引,且使用上索引。使用上索引包括使用该字段,且不会有索引失效的情况出现

设置足够大的join_buffer_size

7 外连接常见问题

Q:如果想筛选驱动表的数据,例如左连接筛选左表的数据,该在连接条件还是where筛选?

A:要通过where筛选,连接条件只影响连接过程,不影响连接返回的结果数(某些情况下连接条件会影响连接返回的结果数,例如左连接中,右侧匹配的数据不唯一的时候)

Q:被驱动表匹配的数据行不唯一导致最终连接数据超过驱动表数据量该怎么办?例如对于左连接,右表匹配的数据行不唯一。

A:join之前先对被驱动表去重,例如通过group by去重:A lef join (select * from B group by name)。

8 参考资料

版权声明

MySQL获取连接_MySQL 连接查询超全详解相关推荐

  1. 前端:深拷贝的多种方法(超全详解)

    别划走 !走过路过不要错过:错过这个村,我在下一个村等你!坚持每天进步一点点:一天两天可能没发现有效果:但一年365天后你将会感谢今天的自己!!! 今天博主带大家了解一下前端常用的深拷贝方法:(超全详 ...

  2. 计算机操作系统超全详解

    操作系统详解 一 为什么要有操作系统 (两本书:现代操作系统.操作系统原理,学好python以后再去研究吧~~) 现代的计算机系统主要是由一个或者多个处理器,主存,硬盘,键盘,鼠标,显示器,打印机,网 ...

  3. GitHub重磅官宣!Java开发环境搭建超全详解

    =>下载虚拟机 由于本次演示是基于windows的,因此下载windows版本的虚拟机 =>安装 下载好的虚拟机安装包如下,双击进行安装. 注意:一般在安装新软件时,尽量把杀毒软件关闭. ...

  4. Java开发环境搭建超全详解

    在项目产品开发中,开发环境搭建是软件开发的首要阶段,也是必须阶段,只有开发环境搭建好了,方可进行开发,良好的开发环境搭建,为后续的开发工作带来极大便利. 对于大公司来说,软件开发环境搭建工作一般是由运 ...

  5. mysql select 缓存_mysql select缓存机制使用详解

    mysql Query Cache 默认为打开.从某种程度可以提高查询的效果,但是未必是最优的解决方案,如果有的大量的修改和查询时,由于修改造成的cache失效,会给服务器造成很大的开销,可以通过qu ...

  6. Java 中的泛型(两万字超全详解)

    文章目录 前言 一.泛型概述 1. 什么是泛型?为什么要使用泛型? 2. 泛型使用场景 3. 泛型概述小结 二.泛型类 1. 泛型类的定义 2. 泛型类的使用 三.泛型接口 四.泛型方法 1. 泛型方 ...

  7. php日期和时间函数,php日期和时间函数超全详解!!!

    PHP日期和时间函数之系统时区设置 在PHP中,有两种更改时区的方法:php 第一种:html 修改php.ini文件中的设置,找打[data]下的;date.timezone = 选项,去掉前面的引 ...

  8. mysql执行计划_mysql的sql执行计划详解

    点击蓝字"程序员考拉"欢迎关注! 引言: 实际项目开发中,由于我们不知道实际查询的时候数据库里发生了什么事情,数据库软件是怎样扫描表.怎样使用索引的,因此,我们能感知到的就只有sq ...

  9. c mysql 设置字符集_MYSQL字符集设置的方法详解(终端的字符集)

    前言 每次利用终端 创建数据库或者创建表的时候,字符集都是latin1,或者进行插入值的时候,显示的是乱码(不指定字符集的时候)如下: 查看当前数据库的字符集 character_set_client ...

最新文章

  1. 免费开源、功能完善、暗黑风格,你会拒绝这款SSH工具吗?
  2. CornerNet 测试:
  3. 王超:奇虎360MongoDB
  4. 关于积累-accumulation
  5. 51nod 1096 距离之和最小 思维题,求中位数
  6. 小论文期刊投稿相关整理资料
  7. BeetleX进程服务管理组件应用
  8. [mybatis]映射文件_参数处理
  9. kali安装nessus_漏洞扫描工具Nessus指南
  10. C语言课后习题(44)
  11. 成都信息工程大学c语言题库,成都信息工程学院C语言考试题及答案.docx
  12. Sentinel热点Key降级下_分布式系统集群限流_线程数隔离_削峰填谷_流量控制_速率控制_服务熔断_服务降级---微服务升级_SpringCloud Alibaba工作笔记0043
  13. mysql for python 64_解决windows64位系统上安装mysql-python报错
  14. 寻找圣杯 In Search of the Holy Grail
  15. 为什么 1KB = 1024Byte???群里讨论。
  16. Git和SourceTree配合使用
  17. vim 查找相同行 删除向同行
  18. 一文学会快速傅里叶变换(FFT)
  19. 最简单的人脸检测(免费调用百度AI开放平台接口)
  20. 3D模型欣赏:美杜莎女妖 角色设计完整 造型独特

热门文章

  1. shell分割参数为数组并循环执行jar,异常时退出循环
  2. oracle8i substr,Oracle中的INSTR,NVL和SUBSTR函数的用法详解
  3. app 模拟器抓包 burpsuite_来看黑客是如何使用Proxifier+burpsuite代理https协议数据包...
  4. textarea 文本框 实现右下角展现 动态字数
  5. 想要预览文件或是图片,将后端返回的信息转换为前端可以正常显示的格式
  6. linux文件替换命令sed使用
  7. 打开Android Studio报错required plugin “Android Support” is disabled
  8. Go36-13-结构体及其方法
  9. [置顶] SPL讲解(6)--Condition篇
  10. Android Error:Could not find lottie.jar