谈一谈什么是 MySQL 中的 join 查询

MySQL中的JOIN操作 Inner Join 返回了员工和部门有匹配关系的记录。Left Join 返回了所有员工的信息,其中赵六没有部门信息,所以department_name字段为NULL。Right Join 返回了所有部门的信息,其中技术部没有员工,所以name字段为NULL。Left Join、Right Join和Inner JoinMySQL中非常重要的查询操作,它们允许我们根据两个或多个表之间的关系来检索数据。在选择使用哪种JOIN操作时,我们应根据具体的业务需求和数据关系来决定。 阅读详情

前引

相信大家 MySQL 都用了很久了,各种 join 查询天天都在写,但是 join 查询到底是怎么查的,怎么写才是最正确的,今天我就和大家一起学习探讨一下

索引对 join 查询的影响

数据准备

假设有两张表 t1、t2,两张表都存在有主键索引 id 和索引字段 a,b 字段无索引,然后在 t1 表中插入 100 行数据,t2 表中插入 1000 行数据进行实验

CREATE TABLE `t2` ( `id` int NOT NULL, `a` int DEFAULT NULL, `b` int DEFAULT NULL, PRIMARY KEY (`id`), KEY `t2_a_index` (`a`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; ​ CREATE PROCEDURE **idata**() BEGIN DECLARE i INT; SET i = 1; WHILE (i <= 1000)do INSERT INTO t2 VALUES (i,i,i); SET i = i +1; END WHILE; END; CALL **idata**(); CREATE TABLE t1 LIKE t2; INSERT INTO t1 (SELECT * FROM t2 WHERE id <= 100); 复制代码

有索引查询过程

我们使用查询 SELECT * FROM t1 STRAIGHT_JOIN t2 ON (t1.a=t2.a);因为 join 查询 MYSQL 优化器不一定能按照我们的意愿去执行,所以为了分析我们选择用 STRAIGHT_JOIN 来代替,从而更直观的进行观察

​可以看出我们使用了 t1 作为驱动表,t2 作为被驱动表,上图的 explain 中显示本次查询用上了 t2 表的字段 a索引,所以这个语句的执行过程应该是下面这样的:

  1. 从 t1 表中读取一行数据 r

  2. 从数据 r中取出字段 a到表 t2 中进行匹配

  3. 取出 t2 表中符合条件的行,和 r组成一行作为结果集的一部分

  4. 重复执行步骤 1-3,直到表 t1 循环数据

该过程称之为 Index Nested-Loop Join,在这个流程里,驱动表 t1 进行了全表扫描,因为我们给 t1 表插入了 100 行数据,所以本次的扫描行数是 100,而进行 join 查询时,对于 t1 表的每一行都需去 t2 表中进行查找,走的是索引树搜索,因为我们构造的数据都是一一对应的,所以每次搜索只扫描一行,也就是 t2 表也是总共扫描 100 行,整个查询过程扫描的总行数是 100+100=200 行。

无索引查询过程

SELECT * FROM t1 STRAIGHT_JOIN t2 ON (t1.a = t2.b); 复制代码

​可以看出由于 t2 表字段 B上没有索引,所以按照上述 SQL 执行时每次从 t1 去匹配 t2 的时候都要做一次全表扫描,这样算下来扫描 t2 多大 100 次,总扫描次数就是 100*1000 = 10 万行。

当然了这个查询结果还是在我们建的这两个都是小表的情况下,如果是数量级 10 万行的表,就需要扫描 100 亿行,这就太恐怖了!

2. 了解Block Nested-Loop Join

Block Nested-Loop Join查询过程

那么被驱动表上没有存在索引,这一切都是怎么发生的呢?

实际上当被驱动表上没有可用的索引,算法流程是这样的:

  1. 把 t1 的数据读取线程内存 join_buffer 中,因为上述我们写的是 select * from,所以相当于是把整个 t1 表放入了内存;

  2. 扫描 t2 的过程,实际上是把 t2 的每一行取出来,跟 join_buffer 中的数据去做对比,满足 join 条件的,作为结果集的一部分进行返回。

​所以结合图 2中 Extra 部分说明 Using join buffer 可以发现这一丝端倪,整个过程中,对表 t1 和t2 都做了一次全表扫描,因此扫描的行数是 100+1000=1100 行,因为 join_buffer 是以无序数组的方式组织的,因此对于表 t2 中每一行,都要做 100 次判断,总共需要在内存中进行的判断次数是 100*1000=10 万次,但是因为这 10 万次是发生在内存中的所以速度上要快很多,性能也更好。

Join_buffer

根据上述已经知道了,没有索引的情况下 MySQL 是将数据读取内存进行循环判断的,那么这个内存肯定不是无限制让你使用的,这时我们就需要用到一个参数 join_buffer_size,该值默认大小 256k,如下图:

SHOW VARIABLES LIKE '%join_buffer_size%'; 复制代码

​假如查询的数据过大一次加载不完,只能够加载部分数据(80 条),那么查询的过程就变成了下面这样

  1. 扫描表 t1,顺序读取数据行放入 join_buffer 中,直至加载完第 80 行满了

  2. 扫描表 t2,把 t2 表中的每一行取出来跟 join_buffer 中的数据做对比,将满足条件的数据作为结果集的一部分返回

  3. 清空 join_buffer

  4. 继续扫描表 t1,顺序读取剩余的数据行放入 join_buffer 中,执行步骤 2

这个流程体现了算法名称中 Block 的由来,分块 join,可以看出虽然查询过程中 t1 被分成了两次放入 join_buffer 中,导致 t2 表被扫描了 2次,但是判断等值条件的次数还是不变的,依然是(80+20)*1000=10 万次。

所以这就是有时候 join 查询很慢,有些大佬会让你把 join_buffer_size 调大的原因。

如何正确的写出 join 查询

驱动表的选择

1、有索引的情况下

在这个 join 语句执行过程中,驱动表是走全表扫描,而被驱动表是走树搜索。

假设被驱动表的行数是 M,每次在被驱动表查询一行数据,先要走索引 a,再搜索主键索引。每次搜索一棵树近似复杂度是以 2为底的 M的对数,记为 log2M,所以在被驱动表上查询一行数据的时间复杂度是 2*log2M。

假设驱动表的行数是 N,执行过程就要扫描驱动表 N 行,然后对于每一行,到被驱动表上 匹配一次。因此整个执行过程,近似复杂度是 N + N2log2M。显然,N 对扫描行数的影响更大,因此应该让小表来做驱动表。

2、那没有索引的情况

上述我知道了,因为 join_buffer 因为存在限制,所以查询的过程可能存在多次加载 join_buffer,但是判断的次数都是 10 万次,这种情况下应该怎么选择?

假设,驱动表的数据行数是 N,需要分 K 段才能完成算法流程,被驱动表的数据行数是 M。这里的 K不是常数,N 越大 K就越大,因此把 K 表示为λ*N,显然λ的取值范围 是 (0,1)。

扫描的行数就变成了 N+λNM,显然内存的判断次数是不受哪个表作为驱动表而影响的,而考虑到扫描行数,在 M和 N大小确定的情况下,N 小一些,整个算是的结果会更小,所以应该让小表作为驱动表

总结:真相大白了,不管是有索引还是无索引参与 join 查询的情况下都应该是使用小表作为驱动表。

什么是小表

还是以上面表 t1 和表 t2 为例子:

SELECT * FROM t1 STRAIGHT_JOIN t2 ON t1.b = t2.b WHERE t2.id <= 50; ​ SELECT * FROM t2 STRAIGHT_JOIN t1 ON t1.b = t2.b WHERE t2.id <= 50; 复制代码

上面这两条 SQL 我们加上了条件 t2.id <= 50,我们使用了字段 b,所以两条 SQL 都没有用上索引,但是第二条 SQL 可以看出 join_buffer 只需要放入前 50 行,显然查询更快,所以 t2 的前 50 行就是那个相对较小的表,也就是我们上面说所说的‘小表’。

再看另一组:

SELECT t1.b,t2.* FROM t1 STRAIGHT_JOIN t2 ON t1.b = t2.b WHERE t2.id <= 100; ​ SELECT t1.b,t2.* FROM t2 STRAIGHT_JOIN t1 ON t1.b = t2.b WHERE t2.id <= 100; 复制代码

这个例子里,表 t1 和 t2 都是只有 100 行参加 join。 但是,这两条语句每次查询放入 join_buffer 中的数据是不一样的: 表 t1 只查字段 b,因此如果把 t1 放到 join_buffer 中,只需要放入字段 b 的值; 表 t2 需要查所有的字段,因此如果把表 t2 放到 join_buffer 中的话,就需要放入三个字 段 id、a 和 b。

这里,我们应该选择表 t1 作为驱动表。也就是说在这个例子里,”只需要一列参与 join 的 表 t1“是那个相对小的表。

结论:

在决定哪个表做驱动表的时候,应该是两个表按照各自的条件过滤,过 滤完成之后,计算参与 join 的各个字段的总数据量,数据量小的那个表,就是“小表”, 应该作为驱动表。

MySQL】提高篇—复杂查询:多表连接(INNER JOIN、LEFT JOIN、RIGHT JOIN、FULL JOIN INNER JOIN:只返回两个表中匹配的记录。LEFT JOIN:返回左表的所有记录及右表中匹配的记录。RIGHT JOIN:返回右表的所有记录及左表中匹配的记录。FULL JOIN:返回两个表中的所有记录(通过 UNION 实现)。这些连接操作在实际应用中非常重要,能够帮助我们从多个表中提取和组合数据,以满足复杂的查询需求。 阅读详情

相关推荐

MySQLJOIN 和子查询的区别与使用场景

本文详细探讨了 MySQLJOIN 和子查询的区别及其最佳使用场景。JOIN 用于基于条件组合多个表的数据,支持多种类型如 INNER JOIN、LEFT JOIN、RIGHT JOIN 和 FULL JOIN,通常性能更优,适合处理大数据集和复杂表关系。子查询则用于嵌套查询,适用于复杂筛选和计算逻辑,但可能影响性能。通过具体代码示例,本文展示了 JOIN 和子查询的不同应用场景,并建议根据实际需求选择合适的工具,以提高查询效率和代码可维护性。

C_V_Better的博客 1354

mysqlJOIN用法详解-附带查询示例

SQL 中,JOIN是用于将多个表中的数据连接在一起的操作。它通过指定连接条件将两个或多个表中符合条件的行组合起来,产生一个新的结果集。SQL 中常见的 JOIN 类型包括和。

厚积薄发. 9473

MySQL中的Join连接查询

我们在进行表连接查询的时候一般都会使用JOIN xxx ON xxx的语法,ON语句的执行是在JOIN语句之前的,也就是说两张表数据行之间进行匹配的时候,会先判断数据行是否符合ON语句后面的条件,再决定是否JOIN,对参与 Join 操作的基表或视图进行过滤,之后再对两表进行 Join 操作,输出结果集。对于三表或多表 Join,则都是可以拆分为两表 Join 的方式进行处理,最先参与 Join 操作的两个表的 Join 的结果集,以表的形式参与后续的 Join 操作。我们来举个例子来简单理解笛卡尔积。

BlueProtocolBlog 2782

mysql join什么时候用

即如果可以使用 Index Nested-Loop Join 算法,也就是说可以用上被驱动表上的索引,其实是没问题的;如果使用 Block Nested-Loop Join 算法,扫描行数就会过多。尤其是在大表上的 join 操作,这样可能要扫描被驱动表很多次,会占用大量的系统资源。所以这种 join 尽量不要用。使用 join 语句,性能比强行拆成多个单表执行 SQL 语句的性能要好;如果使用 join 语句的话,需要让小表做驱动表 但是结论前提是“可以使用被驱动表的索引”

weixin_46856386的博客 215

MySQL】连接查询JOIN 关键字)—— 图文详解:内连接(INNER JOIN)、外连接(OUTER JOIN)、左连接(LEFT JOIN)、左外连接、右连接、右外连接、全连接、全外连接

MySQL】连接查询JOIN 关键字)—— 图解:内连接(INNER JOIN)、等值连接、自然连接(NATURAL JOIN)、交叉连接、外连接(OUTER JOIN)、左连接(LEFT JOIN)、左外连接(LEFT OUTER JOIN)、右连接(RIGHT JOIN)、右外连接(RIGHT OUTER JOIN)、全连接、全外连接。

我梦 7723

javamysql多表查询 JOIN ON 语句

本文章案例是基于,SpringBoot + MyBatisPlus开发的项目 我这里给出两个案例: (1)一个字段关联 (2)多个字段关联 ### 二、一个字段关联 现有一个Post类,数据库对应为tb_post,其中有一个user_id字段,对应sys_user表中的user_id字段,现需要将user_id对应的user_name查询出来和其他字段一起返回给前端。 1、新建DTO类 我们根据POST类,创建一个PostDTO类,PostDTO类中,复制Post的所有代码,只新增一行private

weilaaer的博客 832

mysql一次查询无关联多个表_谈一谈我为什么不建议在Mysql中用join或关联子查询来实现多表查询-Fun言...

前言在项目开发中,有很多同学喜欢使用join,left join等来实现多表查询,其实我是不建议这样用的,我更习惯于把一条冗余的sql分解成很多条sql来进行查询,这时就有人问了,这样不就更复杂了吗,明明一条sql就可以解决,为什么要分成这么多条,显得很low,其实不然,很多表的联查并不会让你炫技,其带来的可能是查询效率的降低,下面我们就说说分解关联查询的优势把Mysql关联子查询缺点1.对于my...

weixin_31842775的博客 908

详解 Mysql LEFT JOINJOIN查询区别及原理

一、Join查询原理 查询原理:MySQL内部采用了一种叫做 nested loop join(嵌套循环连接)的算法。Nested Loop Join 实际上就是通过驱动表的结果集作为循环基础数据,然后一条一条的通过该结果集中的数据作为过滤条件到下一个表中查询数据,然后合并结果。如果还有第三个参与 Join,则再通过前两个表的 Join 结果集作为循环基础数据,再一次通过循环查询条件到第三个表中查询数据,如此往复,基本上MySQL采用的是最容易理解的算法来实现join。所以驱动表的选择非常重要,驱动表的数据

LoveSummer 2万+

MySQL 高级查询JOIN、子查询、窗口函数

通过深入掌握这三种高级查询技术,你可以大幅提升 MySQL 查询的复杂度与灵活性,从而更好地支持复杂业务场景和数据分析需求。这里,**CTE(公用表表达式)**先统计出每个销售人员在各个区域内的订单总额,然后使用窗口函数按区域进行分区并对总销售额进行排名,帮助管理者快速识别出每个区域的销售冠军。JOIN 允许我们在 SQL 语句中将两个或多个表通过相关联的列进行组合,从而在一条查询中获取多表数据。子查询(Subquery)是嵌套在其他 SQL 语句内部的查询语句,通常用于将一个查询的结果作为条件或数据源。

wangqiang0921的博客 3246

理解 MySQL 中的 JOIN 与 UNION

理解 MySQL 中的 JOIN 与 UNION 文章目录理解 MySQL 中的 JOIN 与 UNION起步开始前的准备JOINNATURAL JOINLEFT JOINRIGHT JOINUNIONUNION ALL 起步 最近公司接到一个项目,任务是根据需求制表。完整过程是:用 SQL 汇总数据,再写进 Execel 文件中。SQL 这门课倒是大学里学过,过久不用,不记得许多,顶多 SEL...

有关心情 1933

MySQL入门·连接查询】12.1 INNER JOIN

INNER JOINMySQL中用于从两个或多个表中返回匹配行的强大工具。了解INNER JOIN的基本语法、工作原理以及性能优化技巧,可以帮助我们更有效地利用MySQL进行数据分析和操作。通过合理使用INNER JOIN,我们可以轻松地组合来自不同表的数据,满足各种复杂的查询需求。同时,我们还需要注意优化查询性能,确保INNER JOIN在实际应用中能够发挥最佳效果。👨‍💻博主Python老吕评论,您的举手之劳将对我提供了无限的写作动力!🤞🔥《跟老吕学Python编程》

Python老吕的博客 3635

MySQL入门·连接查询】12.4 CROSS JOIN

CROSS JOINMySQL中一种强大的连接操作,它允许您获取两个或多个表的笛卡尔积。然而,在使用它时需要特别注意性能问题和查询逻辑的设计。通过优化查询、限制结果集大小、使用索引以及仔细选择使用场景,您可以有效地利用CROSS JOIN来满足您的数据检索需求。同时,不要忘记了解其他JOIN类型,以便在需要时选择最适合您查询需求的连接方式。通过深入理解CROSS JOIN的工作原理、优化策略以及应用场景,您将能够更好地利用这一功能来执行复杂的查询操作并提取所需的数据。

Python老吕的博客 4363

MySQL join查询的原理

MySQL join查询的原理MySQL用Nested-Loop Join算法实现join查询Nested-Loop Join有三种实现SNLJBNLJINLJ聚集索引非聚集索引NLJ优先级如何优化join查询效率 MySQL用Nested-Loop Join算法实现join查询 区分驱动表和被驱动表,以驱动表的结果集为循环的基础, 访问被驱动表过滤数据,然后合并结果,驱动表在外循环、被驱动表在内循环。 如果还有第三张参与join查询的表, 则以合并的结果为驱动表,第三张表作为被驱动表,以此类推。 left

CaptainCats的博客 2229

join查询可以⽆限叠加吗?MySQLjoin查询有什么限制吗?

关注威哥爱编程,全栈路上你就行。

威哥爱编程,华为 HDE,鸿蒙极客、CSDN博客专家、《HarmonyOS NEXT 开发之路》系列图书作者 1253

mysql关于join查询优化的方法

MySQLJOIN性能优化核心在于。

u012875451的博客 1555

MySQL数据库JOIN查询优化

文章摘要:本文探讨了MySQL多表JOIN操作的性能问题及优化方案。通过构建包含5万用户、50万订单、100商品和百万订单详情的测试数据库,作者验证了阿里开发规范中关于JOIN限制的合理性。重点分析了数据库优化器局限性、JOIN算法性能瓶颈(nested loop/block nested loop/index nested loop)、分库分表架构影响等核心问题,并指出数据类型一致性和索引对性能的关键作用。文章还揭示了临时表生成、数据分布不均、数据库参数配置等潜在性能影响因素。

weixin_62307424的博客 892

MySQL join查询优化

在日常的开发中,我们经常遇到这样情况:select * from TableA inner join TableB...它响应速度一直很快的,随着数据的增长,突然有一天开始很慢了。那该怎么破? 对,驱动表是突破口, 1. 那什么是驱动表呢? 指定了联接条件时,满足查询条件的记录行数少的表为驱动表 未指定联接条件时,行数少的表为驱动表(Important!) 如果你搞不清楚该让谁做驱动表、...

布道 6997
上一篇: MySQL8.0索引新特性—支持降序索引与支持隐藏索引
下一篇: 你在MySQL中加了什么锁,导致死锁的?
Java_LingFeng
博客等级 码龄4年 501粉丝 259原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值