一、Join查询原理

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

所以驱动表的选择非常重要,驱动表的数据小可以显著降低扫描的行

一般情况下参与联合查询的两张表都会一大一小,如果是join,在没有其他过滤条件的情况下MySQL会自动选择小表作为驱动表。简单来说,驱动表就是主表,left join 中的左表就是驱动表,right join 中的右表是驱动表。

二、Nested-Loop Join

如 select * from t1 inner join t2 on t1.id=t2.tid ,t1称为外层表,也可称为驱动表,t2称为内层表,也可称为被驱动表

mysql只支持一种join算法:Nested-Loop Join(嵌套循环连接),但Nested-Loop Join有三种变种:

  • 简单嵌套循环连接:Simple Nested-Loop Join(SNLJ)
  • 索引嵌套循环连接:Index Nested-Loop Join(INLJ)
  • 缓存块嵌套循环连接:Block Nested-Loop Join(BNLJ)

在选择Join算法时,会有优先级,理论上会优先判断能否使用INLJ、BNLJ: Index Nested-LoopJoin > Block Nested-Loop Join > Simple Nested-Loop Join

2.1 Simple Nested-Loop Join

如下图,r为驱动表,s为匹配表,可以看到从r中分别取出r1、r2、…、rn去匹配s表的左右列,然后再合并数据,对s表进行了rn次访问,对数据库开销大。

如果table1有1万条数据,table2有1万条数据,那么数据比较的次数=1万 * 1万 =1亿次,这种查询效率会非常慢。

故基本不使用这种方式,mysql会根据情况选择其他两种方式进行查询

2.2 Index Nested-Loop Join(减少内层表数据的匹配次数)

  • 索引嵌套循环连接是基于索引进行连接的算法,索引是基于内层表的,通过外层表匹配条件直接与内层表索引进行匹配,避免和内层表的每条记录进行比较, 从而利用索引的查询减少了对内层表的匹配次数,优势极大的提升了 join的性能
  • 使用场景:只有内层表join的列有索引时,才能用到Index Nested-LoopJoin进行连接
  • 由于用到索引,如果索引是辅助索引而且返回的数据还包括内层表的其他数据,则会回内层表查询数据,多了一些IO操作

这个要求非驱动表(匹配表s)上有索引,可以通过索引来减少比较,加速查询。

在查询时,驱动表(r)会根据关联字段的索引进行查找,当在索引上找到符合的值,再回表进行查询,也就是只有当匹配到索引以后才会进行回表查询。

如果非驱动表(s)的关联健是主键的话,性能会非常高,如果不是主键,要进行多次回表查询,先关联索引,然后根据二级索引的主键ID进行回表操作,性能上比索引是主键要慢。

2.3 Block Nested-Loop Join(减少内层表数据的循环次数)

缓存块嵌套循环连接通过一次性缓存多条数据,把参与查询的列缓存到Join Buffer 里,然后拿join buffer里的数据批量与内层表的数据进行匹配,从而减少了内层循环的次数(遍历一次内层表就可以批量匹配一次Join Buffer里面的外层表数据)。

当不使用Index Nested-Loop Join的时候(内层表查询不适用索引),默认使用Block Nested-Loop Join

  1. Join Buffer会缓存所有参与查询的列而不是只有Join的列
  2. 可以通过调整join_buffer_size缓存大小
  3. join_buffer_size的默认值是256K,join_buffer_size的最大值在MySQL 5.1.22版本前是4G-1,而之后的版本才能在64位操作系统下申请大于4G的Join Buffer空间
  4. 使用Block Nested-Loop Join算法需要开启优化器管理配置的optimizer_switch的设置block_nested_loop为on,默认为开启

三 优化

  1. 用小结果集驱动大结果集,减少外层循环的数据量:如果小结果集和大结果集连接的列都是索引列,mysql在内连接时也会选择用小结果集驱动大结果集,因为索引查询的成本是比较固定的,这时候外层的循环越少,join的速度便越快
  2. 为匹配的条件增加索引:争取使用INLJ,减少内层表的循环次数
  3. 增大join buffer size的大小:当使用BNLJ时,一次缓存的数据越多,那么外层表循环的次数就越少
  4. 减少不必要的字段查询:
    • 当用到BNLJ时,字段越少,join buffer 所缓存的数据就越多,外层表的循环次数就越少
    • 当用到INLJ时,如果可以不回表查询,即利用到覆盖索引,则可能可以提示速度
  5. 尽量使用inner join,避免left join 和NULL

摘抄自(有删改):https://blog.csdn.net/agonie201218/article/details/106993948

四 补充

4.1 找出所有在左表,不在右表的纪录

注:列值为null应该用is null 而不能用=NULL

  a.user_id 列必须声明为 NOT NULL 的

select id, name, action from user as u left join user_action a on u.id = a.user_id where a.user_id is NULL

4.2 where使用

在连表查询时,通常在子查询中使用 WHERE 子句限制表数据会比在最后的主查询中使用 WHERE 子句效率更高

原因是,在子查询中使用 WHERE 子句可以对较小的结果集进行过滤,减少了主查询需要处理的数据量。这样可以减少数据的传输和处理的负担,提高查询性能。

另一方面,在最后的主查询中使用 WHERE 子句时,会先执行联接操作并获取所有的匹配行,然后再对整个结果集进行过滤。这样会导致在查询过程中处理了更多的数据,增加了查询的开销,可能导致性能下降。

因此,将限制条件放在子查询中,可以尽早地过滤数据,只将需要的数据传递到后续的查询阶段,减少了不必要的计算和数据传输,更加高效。

SELECT A.*
FROM 大表A A
JOIN (SELECT * FROM 小表B WHERE C) AS B ON A.关联键 = B.关联键;

4.2 多表联合查询

在MySQL关联查询中,建议将小表作为驱动表,而将大表作为被驱动表

因为小表通常拥有较少的记录,而大表可能包含大量的数据。当小表作为驱动表时,MySQL可以先从小表中获取数据,并使用这些数据来查询大表。这样做的好处是可以减少查询的数据量,提高查询的效率。如果将大表作为驱动表,MySQL需要先扫描大表的所有记录,然后再根据关联条件查询小表,这样会增加查询的时间和资源消耗。

需要注意的是,选择驱动表的原则是根据查询的条件和数据量来决定,而不是根据表的大小。有时候大表可能也是更适合作为驱动表的,这需要根据具体的情况进行分析和测试。

假设t4为大表:

select
b.name,
c.view_name,
a.notify_time,
a.product,
a.alarm_id
from
(
select
psa,
group_id
from
t1
) a
left join (
select
id,
name
from
t2
) b on a.group_id=b.id
left join (
select
view_name,
psas
from
t3
where
type='alarm'
) c on concat('"', c.psas, '"') regexp a.psa
left join (
select
notify_time,
product,
alarm_id
from
t4
where
product!='/'
) d on d.product=b.psa

Mysql--JOIN连表查询的更多相关文章

  1. MySQL之多表查询一 介绍 二 多表连接查询 三 符合条件连接查询 四 子查询 五 综合练习

    MySQL之多表查询 阅读目录 一 介绍 二 多表连接查询 三 符合条件连接查询 四 子查询 五 综合练习 一 介绍 本节主题 多表连接查询 复合条件连接查询 子查询 首先说一下,我们写项目一般都会建 ...

  2. day15(mysql 的多表查询,事务)

    mysql之多表查询 1.合并结果集 作用:合并结果集就是把两个select语句查询的结果连接到一起! /*创建表t1*/ CREATE TABLE t1( a INT PRIMARY KEY , b ...

  3. mysql数据库优化课程---11、mysql普通多表查询

    mysql数据库优化课程---11.mysql普通多表查询 一.总结 一句话总结:select user.username,user.age,class.name,class.ctime from u ...

  4. Mariadb/MySQL数据库单表查询基本操作及DML语句

    Mariadb/MySQL数据库单表查询基本操作及DML语句 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一数据库及表相关概述 1>.数据库操作 创建数据库: CREATE ...

  5. day 39 MySQL之多表查询

    MySQL之多表查询   阅读目录 一 介绍 二 多表连接查询 三 符合条件连接查询 四 子查询 五 综合练习 一 介绍 本节主题 多表连接查询 复合条件连接查询 子查询 首先说一下,我们写项目一般都 ...

  6. MySQL的联表查询

    MySQL的联表查询 首选:分析查询的字段来自哪些表 进而:确定交集 然后:确定判断的条件 比如:从student表 和 result表 查学号.考试名称.学时.考试日期.考试成绩 表1: 学号 考试 ...

  7. 对于大量left join 的表查询,可以在关键的 连接节点字段上创建索引。

    对于大量left join 的表查询,可以在关键的 连接节点字段上创建索引. 问题: 大量的left join 怎么优化 select a.id,a.num,b.num,b.pcs,c.num, c. ...

  8. Vc数据库编程基础MySql数据库的表查询功能

    Vc数据库编程基础MySql数据库的表查询功能 一丶简介 不管是任何数据库.都会有查询功能.而且是很重要的功能.上一讲知识简单的讲解了表的查询所有. 那么这次我们需要掌握的则是. 1.使用select ...

  9. mybatis逆向工程,实现join多表查询,避免多表相同字段名的陷阱

    ​ mybatis逆向工程,实现join多表查询,避免多表相同字段名的陷阱 ​ 前言:使用 mybatis generator 生成表格对应的pojo.dao.mapper,以及对应的example的 ...

  10. MySQL之单表查询 一 单表查询的语法 二 关键字的执行优先级(重点) 三 简单查询 四 WHERE约束 五 分组查询:GROUP BY 六 HAVING过滤 七 查询排序:ORDER BY 八 限制查询的记录数:LIMIT 九 使用正则表达式查询

    MySQL之单表查询 阅读目录 一 单表查询的语法 二 关键字的执行优先级(重点) 三 简单查询 四 WHERE约束 五 分组查询:GROUP BY 六 HAVING过滤 七 查询排序:ORDER B ...

随机推荐

  1. AtCoder F - Parenthesis Checking

    原题链接:AtCoder F - Parenthesis Checking 一个全由\('('\)和\(')'\)构成的字符串,由以下两个操作: 1 l r交换字符串第\(l\)个和第\(r\)个字符 ...

  2. 一个Blazor+WinForm+MAUI+PDA实现的条码比对系统

    条码比对系统是由单机版桌面软件和Android版的PDA扫码软件组成,桌面软件采用Blazor与WinForm进行混合开发,PDA扫码软件采用MAUI进行开发,这个项目都是基于.NET技术进行构建,这 ...

  3. 练习感受以及经验总结(ing)

    connection = DriverManager.getConnection(url, user,password );顺序一定不能搞错,当时顺序写错了.查bug查了一晚上.得益于两个函数都用到了 ...

  4. CSS(不定时更新)

    一.使用img后的高度多了4px 由于img是行内元素,默认display: inline; 它与文本的默认行为类似,下边缘是与基线(baseline)对齐,而不是紧贴容器下边缘. 将displayp ...

  5. Detecting Unknown Encrypted Malicious Traffic in Real Time via Flow Interaction Graph Analysis

    1 前言 1.1 标题 Detecting Unknown Encrypted Malicious Traffic in Real Time via Flow Interaction Graph An ...

  6. Illegal overloaded getter method with ambiguous type for property......

    问题原因 建议 1.boolean类型的属性值不建议设置为is开头,否则会引起rpc框架的序列化异常. 2.如果强行将IDE自动生成的isSuccess()方法修改成getSuccess(),也能获取 ...

  7. 16、strconv

    1.strconv是什么? strconv是用来处理字符串和基本类型之间的转换的 2.strconv的使用 /** * @author ly (个人博客:https://www.cnblogs.com ...

  8. Celery架构和主要模块

    **任务模块 ** 包含异步任务和定时任务.其中,异步任务通常在业务逻辑中被触发并发往任务队列,而定时任务由 Celery Beat 进程周期性地将任务发往任务队列. **消息中间件 Broker * ...

  9. vue3组合式api生命周期

    生命周期钩子函数 Vue3:https://cn.vuejs.org/api/composition-api-lifecycle.html Vue2:https://v2.cn.vuejs.org/v ...

  10. 假如这个地方可能为null,那他一定会为null

    假如你的代码,在某个地方(比如controller层)提示你:这个方法调用可能会产生null,那么千万不要视而不见,在某一瞬间它一定会是null,势必报错. /** * 修改保存管理员 */ @Pos ...