MySQL 连接查询超级详解
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中文网其他相关文章!

热AI工具

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

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

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

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

热门文章

热工具

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

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

MySQL在Web应用中的主要作用是存储和管理数据。1.MySQL高效处理用户信息、产品目录和交易记录等数据。2.通过SQL查询,开发者能从数据库提取信息生成动态内容。3.MySQL基于客户端-服务器模型工作,确保查询速度可接受。

Laravel 是一款 PHP 框架,用于轻松构建 Web 应用程序。它提供一系列强大的功能,包括:安装: 使用 Composer 全局安装 Laravel CLI,并在项目目录中创建应用程序。路由: 在 routes/web.php 中定义 URL 和处理函数之间的关系。视图: 在 resources/views 中创建视图以呈现应用程序的界面。数据库集成: 提供与 MySQL 等数据库的开箱即用集成,并使用迁移来创建和修改表。模型和控制器: 模型表示数据库实体,控制器处理 HTTP 请求。

MySQL和phpMyAdmin是强大的数据库管理工具。1)MySQL用于创建数据库和表、执行DML和SQL查询。2)phpMyAdmin提供直观界面进行数据库管理、表结构管理、数据操作和用户权限管理。

MySQL与其他编程语言相比,主要用于存储和管理数据,而其他语言如Python、Java、C 则用于逻辑处理和应用开发。 MySQL以其高性能、可扩展性和跨平台支持着称,适合数据管理需求,而其他语言在各自领域如数据分析、企业应用和系统编程中各有优势。

在开发一个小型应用时,我遇到了一个棘手的问题:需要快速集成一个轻量级的数据库操作库。尝试了多个库后,我发现它们要么功能过多,要么兼容性不佳。最终,我找到了minii/db,这是一个基于Yii2的简化版本,完美地解决了我的问题。

文章摘要:本文提供了详细分步说明,指导读者如何轻松安装 Laravel 框架。Laravel 是一个功能强大的 PHP 框架,它 упростил 和加快了 web 应用程序的开发过程。本教程涵盖了从系统要求到配置数据库和设置路由等各个方面的安装过程。通过遵循这些步骤,读者可以快速高效地为他们的 Laravel 项目打下坚实的基础。

MySQL的基本操作包括创建数据库、表格,及使用SQL进行数据的CRUD操作。1.创建数据库:CREATEDATABASEmy_first_db;2.创建表格:CREATETABLEbooks(idINTAUTO_INCREMENTPRIMARYKEY,titleVARCHAR(100)NOTNULL,authorVARCHAR(100)NOTNULL,published_yearINT);3.插入数据:INSERTINTObooks(title,author,published_year)VA

在使用Thelia开发电商网站时,我遇到了一个棘手的问题:MySQL模式设置不当,导致某些功能无法正常运行。经过一番探索,我找到了一个名为TheliaMySQLModesChecker的模块,它能够自动修复Thelia所需的MySQL模式,彻底解决了我的困扰。
