1 作用
在数据库中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
筛选,所以在使用join
的时候,我们要尽可能提供连接的条件,而少用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 <a, b>; } } }
当然,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 <a, b>; } } }
相比于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 <a, tid.Index所在行>; } }
总共需要循环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)
。
相关学习推荐:mysql视频教程
以上是MySQL 连接查询超级详解的详细内容。更多信息请关注PHP中文网其他相关文章!

MySQL通过异步、半同步和组复制三种模式处理数据复制。1)异步复制性能高但可能丢失数据。2)半同步复制提高数据安全性但增加延迟。3)组复制支持多主复制和故障转移,适用于高可用性需求。

EXPLAIN语句可用于分析和提升SQL查询性能。1.执行EXPLAIN语句查看查询计划。2.分析输出结果,关注访问类型、索引使用情况和JOIN顺序。3.根据分析结果,创建或调整索引,优化JOIN操作,避免全表扫描,以提升查询效率。

使用mysqldump进行逻辑备份和MySQLEnterpriseBackup进行热备份是备份MySQL数据库的有效方法。1.使用mysqldump备份数据库:mysqldump-uroot-pmydatabase>mydatabase_backup.sql。2.使用MySQLEnterpriseBackup进行热备份:mysqlbackup--user=root--password=password--backup-dir=/path/to/backupbackup。恢复时,使用相应的命

MySQL慢查询的主要原因包括索引缺失或不当使用、查询复杂度、数据量过大和硬件资源不足。优化建议包括:1.创建合适的索引;2.优化查询语句;3.使用分表分区技术;4.适当升级硬件。

MySQL视图是基于SQL查询结果的虚拟表,不存储数据。1)视图简化复杂查询,2)增强数据安全性,3)维护数据一致性。视图是数据库中的存储查询,可像表一样使用,但数据动态生成。

mysqldiffersfromothersqldialectsinsyntaxforlimit,自动启动,弦乐范围,子征服和表面上分析。1)MySqluessLipslimit,whilesqlserverusestopopandoraclesrontersrontsrontsrontsronnum.2)

MySQL分区能提升性能和简化维护。1)通过按特定标准(如日期范围)将大表分成小块,2)物理上将数据分成独立文件,3)查询时MySQL可专注于相关分区,4)查询优化器可跳过不相关分区,5)选择合适的分区策略并定期维护是关键。

在MySQL中,如何授予和撤销权限?1.使用GRANT语句授予权限,如GRANTALLPRIVILEGESONdatabase_name.TO'username'@'host';2.使用REVOKE语句撤销权限,如REVOKEALLPRIVILEGESONdatabase_name.FROM'username'@'host',确保及时沟通权限变更。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

PhpStorm Mac 版本
最新(2018.2.1 )专业的PHP集成开发工具

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境

DVWA
Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

Atom编辑器mac版下载
最流行的的开源编辑器

记事本++7.3.1
好用且免费的代码编辑器