MySQL两个表的亲密接触-连接查询的原理

MySQL对于被驱动表的关联字段没索引的关联查询,一般都会使用 BNL 算法。如果有索引一般选择 NLJ 算法,有 索引的情况下 NLJ 算法比 BNL算法性能更高。

关系型数据库还有一个重要的概念:Join(连接)。使用Join有好处,也会坏处,只有我们明白了其中的原理,才能更多的使用Join。切记不可以:

业务之上,再复杂的查询也在一个连表语句中完成。

敬而远之,DBA每次上报的慢查询都是连接查询导致的,我再也不用了。

连接的本质

我们先来创建两个简单的表,再初始化一些数据

CREATE TABLE t1 (m1 int, n1 varchar(1));

CREATE TABLE t2 (m2 int, n2 varchar(1));

INSERT INTO t1 VALUES(1, 'a'), (2 , 'b') ,(3 ,'c') ;
 
INSERT INTO t2 VALUES(2 , 'b'), (3 , 'c '),(4 , 'd');

从本质上来说,连接就是把各个表的数据都取出来进行匹配,t1 和 t2 的两个表连接起来就是这样的:

图片

连接语法:

select * from t1, t2;

如果乐意,我们可以连接任意数量的表。但是如果不加任何限制条件的话,这个数据量是非常大的,我们现实中使用都是会加上限制条件的。我们来看下下面这条语句

select * from t1,t2 where t1.m1 > 1 and t1.m1 = t2.m2 and t2.n2 = 'c';

这个连接查询的执行过程大致如下

首先确定第一个需要查询 表称为驱动表(t1)

步骤1中从驱动表 (t1) 中每获得一条记录,都要去被驱动表 (t2) 中查询匹配。

从上面的步骤,可以看出上述的连表查询我们需要查询一次t1,两次t2。也就是说,两表的连接查询中,需要查询一次驱动表,被驱动表需要查询多次。

这里需要注意下,并不是将所有满足条件的驱动表记录先查询出来放到一个地方,然后再去被驱动表中查询,(如果满足条件的驱动表中的数据非常多,那要需要多大的内存呀。) 所以是每获得一条驱动表记录就去被驱动表中查询。

内连接和外连接

我们再来创建两个表,并插入一些数据

CREATE TABLE student ( 
number INT NOT NULL Auto_increment comment'学号',
name varchar (5) COMMENT '姓名',
major varchar (30) comment '专业',
PRIMARY KEY (number));

CREATE TABLE score ( 
number INT  comment'学号',
subject varchar (30) COMMENT '科目',
score TINYINT  comment '成绩',
PRIMARY KEY (number, subject));


INSERT INTO `student` (`number`, `name`, `major`) 
VALUES ('20230301', '小赵', '计算机科学');
INSERT INTO `student` (`number`, `name`, `major`) 
VALUES ('20230302', '小钱', '通信');
INSERT INTO `student` (`number`, `name`, `major`) 
VALUES ('20230303', '小孙', '土木工程');

INSERT INTO `score` (`number`, `subject`, `score`) 
VALUES ('20230301', '高等数学', '60');
INSERT INTO `score` (`number`, `subject`, `score`) 
VALUES ('20230301', '英语', '70');
INSERT INTO `score` (`number`, `subject`, `score`) 
VALUES ('20230302', '高等数学', '80');
INSERT INTO `score` (`number`, `subject`, `score`) 
VALUES ('20230302', '英语', '90');

如果我们想把所有的学生的成绩都查出来,只需要这样执行:

select s1.number, s1.name, s1.major, s2.subject, s2.score 
  from student as s1 , score as s2 
where s1.number = s2.number;

有个问题就是小孙因为某些原因没有参加考试,所以在结果表中没有对应 的成绩记录。如果老师想查看所有学生的考试成绩,即使是缺考的学生 他们的成绩也应该展示出来。

为了解决这个问题,就有了内连接和外连接的概念:

  • 对于内连接的两个表,若驱动表中的记录在被驱动表找不到匹配的记录,则该记录不会加入到最后的结果集。前面提到的连接都是内连接。

  • 对于外连接的两个表,时驱动表中的记录在被驱动表中没有匹配的记录,也仍然需要加入到结果集。

MySQL 中,根据选取的驱动表的不同,外连接可以细分为

  • 左外连接 选取左侧的表为驱动表。

  • 右外连接·选取右侧的表为驱动表。

当我们使用外连接的时候 有时候我们也不想把驱动表的全部记录都加入到最后的结果集中,这个时候我们就要使用过滤条件了。

• WHERE 子句中的过滤条件:不论是内连接还是外连接 凡是不符合 WHERE 子句中过滤条件的记录都不会被加入到最后的结果集。

• ON 子句中的过滤条件:对于外连接的驱动表中的记录来说,如果无法在被驱动表中找到匹配 ON 子句 中过滤条件的记录 那么该驱动表记录仍然会被加入到结果集中,对应的被驱动表记录的各个字段使用NULL 值填充。

所以上述的需求我们可以左查询这样来做:

select s1.number, s1.name, s1.major, s2.subject, s2.score 
  from student as s1 left join score as s2 
on s1.number = s2.number;

语法:

#左连接
select * from t1 left join t2 on '连接条件' where '普通过滤条件'
#右连接
select * from t1 right join t2 on '连接条件' where '普通过滤条件'

内连接的另一种写法,也是常用写法

select s1.number, s1.name, s1.major, s2.subject, s2.score 
  from student as s1 inner join score as s2 
where s1.number = s2.number;

语法:

select * from t1 inner join t2 on '连接条件' where '过滤条件'

连接原理

上述说了这么多,知识简单回顾一下连接,左连接,右连接这些概念。接下来我们重点说一下 MySQL 采用了什么样的算法来进行表与表之前的连接。

Nested-Loop Join (嵌套循环连接) NLJ

前面我们已经介绍过了执行连接查询的大致步骤了,我们再来简单回顾一下

  • 步骤1:选取驱动表,使用相关的过滤条件,选取代价最低的单表访问方法来执行访问。

  • 步骤2:对步骤1中查询到的驱动表结果中的每一条记录,都分别在被驱动表中匹配符合条件的记录。

  • 如果有三个表,那么步骤2中得到的结果集就像是新的驱动表,然后第三个表就成为了驱动表,重复上述的过程。

整个过程就像是一个嵌套循环,所以这种连接方式称为 嵌套循环连接 ,这是最简单也是最笨的一种连接查询算法。大致处理过程如下:

for each row in t1 matching range {
  for each row in t2 matching reference key {
    for each row in t3 {
      if row satisfies join conditions, send to client
    }
  }
}

需要注意的是对于获套循环连接算法法来说,每当我们从驱动表中得到了一条记录时,就根据这条记录立时到被驱动表中查询一次,如果得到了匹配的记录, 就把组合后 的记录发送给客户端,然后再到驱动表中获取下一条记录。这个过程将重复进行。

有什么方式可以优化吗

使用索引加快连接速度

这个是我们比较熟悉的方式,也是相对来说最有用的方式,在被驱动表上创建合适的索引,只返回必要的字段等都可以起到一些优化的作用。

Block Nested-Loop Join(块嵌套循环连接)BNL

每次访问被驱动表,其表中的记录都会被加载到内存中,然后再从驱动表中取出一条与其匹配,匹配结束后清楚内存,然后再从驱动表中加载一条记录,然后把被驱动表的记录加载到内存匹配,如果这个被驱动表中的数据特别多而且不能使用索引进行访问,那就相当于要从磁盘上读这个表好多次,这个IO的代价就非常大了。所以我们得想办法,尽量减少被驱动表的访问次数,于是就出现了下面这种方式。

不再是逐条获取驱动表的数据,而是一块一块的获取,引入join buffer 缓冲区, 将驱动表join 相关的部分数据列(大小受join buffer的限制)缓存到 join buffer中,然后开始扫描被驱动表,被驱动表的每一条记录一次性和join buffer中所有的驱动表记录进行匹配(内存中操作)。将简单嵌套循环中的多次比较合并成一次,降低了备驱动表的访问频率。

这里缓存的不只是关联表的列,select后面的列也会缓存起来。所以查询的时候尽量减少不必要的字段,可以让join buffer中可以存放更多的列。

join_buffer_size的最大值在32为系统中可以申请4G,在64为操作系统中可以申请大于4G的空间。

图片

MySQL对于被驱动表的关联字段没索引的关联查询,一般都会使用 BNL 算法。如果有索引一般选择 NLJ 算法,有 索引的情况下 NLJ 算法比 BNL算法性能更高。

关联查询优化总结

  1. 超过三个表禁止 join。【阿里巴巴JAVA开发手册】

  2. 需要 join 的字段,数据类型必须绝对一致;【阿里巴巴JAVA开发手册】

  3. 多表关联查询时,保证被关联的字段需要有索引,尽量选择NLJ算法。【阿里巴巴JAVA开发手册】

  4. 小表驱动大表,写多表连接sql时如果明确知道哪张表是小表可以用straight_join写法固定连接驱动方式,省去mysql优化器自己判断的时间

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/344221.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【操作系统】实验四 增加Linux系统调用

🕺作者: 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux 😘欢迎关注:👍点赞🙌收藏✍️留言 🏇码字不易,你的👍点赞🙌收藏❤️关注对我真的很重要&…

Zabbix 整合 Prometheus:案例分享与操作指南

一、简介 Zabbix 和 Prometheus 都是流行的开源监控工具,它们各自具有独特的优势。Zabbix 主要用于网络和系统监控,而 Prometheus 则专注于开源的分布式时间序列数据库。在某些场景下,将这两个工具整合在一起可以更好地发挥它们的优势&#…

C语言进阶——数据结构之链表

前言 hello,大家好呀,我是Humble 在之前的两篇博客,我们学完了数据结构中的顺序表,还对它进行了一个应用,做了一个通讯录的小项目 那今天我们再来学习一个新的数据结构——链表 引入 我们来回忆一下顺序表 对于顺…

Spring Boot 初始(快速搭建 Spring Boot 应用环境)

提示: ① 通过下面的简介可以快速的搭建一个可以运行的 Spring Boot 应用(估计也就2分钟吧),可以简单的了解运行的过程。 ② 建议还是有一点 Spring 和 SpringMVC的基础(其实搭建一个 Spring Boot 环境不需要也没有关系…

C++多线程_std::future与std::promise

目录 1. 引言 2. promise/future的含义 std::future std::promise std::packaged_task std::async 处理异常 std::shared_future 实战:多线程实现快速排序 时钟与限定等待时间 参考: 1. 引言 在并发编程中,我们通常会用到一组非阻塞的模型&a…

共享wifi项目到底能不能做?

如今,互联网已经渗透到我们生活的方方面面,人们对WiFi的需求越来越大,已经成为人们不可或缺的一部分。在这样的背景下,共享WiFi项目应运而生,作为近年来兴起的创业选择,成为了越来越多创业者追逐的热门项目…

Servlet 与 MVC

主要内容 Servlet 重点 MVC 重点 Filter 重点 章节目标 掌握 Servlet 的作用 掌握 Servlet 的生命周期 掌握 JSP 的本质 掌握 MVC 的设计思想 掌握 Filter 的作用及使用场景 第一节 Servlet 1. Servlet 概念 Servlet 是在服务器上运行的能够对客户端请求进行处理&a…

微信小程序(八)图片的设定

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.图片的三种常见缩放形式 2.图片全屏预览 源码&#xff1a; testImg.wxml <!-- 默认状态&#xff0c;不保证缩放比&#xff0c;完全拉伸填满容器 --> <image class"pic" mode"scaleTo…

Backtrader 文档学习-Target Orders

Backtrader 文档学习-Target Orders 1. 概述 sizer不能决定操作是买还是卖&#xff0c;意味着需要一个新的概念&#xff0c;通过增加小智能层可以决定买卖&#xff0c;即通过持仓份额可以决定买卖操作。 这就是策略中order_target_xxx方法族的作用。受zipline的方法的启发&am…

商城系统中30分钟未付款自动取消订单怎么实现(简单几种方法)

实现以上功能 方法1&#xff1a;定时任务批量执行 写一个定时任务&#xff0c;每隔 30分钟执行一次&#xff0c;列出所有超出时间范围得订单id的列表 AsyncScheduled(cron "20 20 1 * * ?")public void cancelOrder(){log.info("【取消订单任务开始】"…

Excel导出警告:文件格式和拓展名不匹配

原因描述&#xff1a; Content-Type 原因&#xff1a;Content-Type&#xff0c;即内容类型&#xff0c;一般是指网页中存在的Content-Type&#xff0c;用于定义网络文件的类型和网页的编码&#xff0c;决定文件接收方将以什么形式、什么编码读取这个文件&#xff0c;这就是经常…

【算法专题】动态规划之路径问题

动态规划2.0 动态规划 - - - 路径问题1. 不同路径2. 不同路径Ⅱ3. 珠宝的最高价值4. 下降路径最小和5. 最小路径和6. 地下城游戏 动态规划 - - - 路径问题 1. 不同路径 题目链接 -> Leetcode -62.不同路径 Leetcode -62.不同路径 题目&#xff1a;一个机器人位于一个 m …

linux安装docker--更具官网教程

1.访问https://docs.docker.com/ 2.进入download 3输入cento 或者直接访问地址Install Docker Engine on CentOS | Docker Docs 4一步一步根据官网命令走 2安装 常见报错&#xff1a; yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.rep…

node.js旅游景点分享网站03796-计算机毕业设计项目选题推荐(附源码)

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。旅游景点分享网站设计&#xff0c;主要的模块包括查看后台首页、轮播图&#xff08;轮播图管理&#xff09;、网站公告管理&#xff08;网站公告…

幻兽帕鲁安装和开服教学

《幻兽帕鲁》游戏热度异常火爆&#xff0c;很多玩家想下载《幻兽帕鲁》和朋友玩&#xff0c;但不知道在哪里能够下载到&#xff0c;下面请看《幻兽帕鲁》下载安装教学&#xff0c;希望能够帮助大家。 幻兽帕鲁》目前仅在PC上的Steam平台发售&#xff0c;可以登录Steam搜索“幻…

Dify学习笔记-基础介绍(一)

1、简介 Dify AI是一款强大的LLMOps&#xff08;Language Model Operations&#xff09;平台&#xff0c;专为用户提供便捷的人工智能应用程序开发体验。 该平台支持GPT系列模型和其他模型&#xff0c;适用于各种团队&#xff0c;无论是用于内部还是外部的AI应用程序开发。 它…

【java问题解决】-word转pdf踩坑

问题情境&#xff1a; 项目中采用word转pdf&#xff0c;最开始使用的pdf相关的apache的pdfbox和itextpdf&#xff0c;后面发现对于有图片背景的word转pdf的情景&#xff0c;word中的背景图会直接占用位置&#xff0c;导致正文不会正确落在背景图上。 解决方案&#xff1a; 采…

力扣日记1.23-【回溯算法篇】17. 电话号码的字母组合

力扣日记&#xff1a;【回溯算法篇】17. 电话号码的字母组合 日期&#xff1a;2023.1.23 参考&#xff1a;代码随想录、力扣 17. 电话号码的字母组合 题目描述 难度&#xff1a;中等 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意…

flink-java使用介绍,flink,java,DataStream API,DataSet API,ETL,设置 jobname

1、环境准备 文档&#xff1a;https://nightlies.apache.org/flink/flink-docs-release-1.17/zh/ 仓库&#xff1a;https://github.com/apache/flink 下载&#xff1a;https://flink.apache.org/zh/downloads/ 下载指定版本&#xff1a;https://archive.apache.org/dist/flink…

洛谷C++简单练习day4

day4---进制转化---1.22 习题概述 题目描述 今天小明学会了进制转换&#xff0c;比如&#xff08;10101&#xff09;2 &#xff0c;那么它的十进制表示的式子就是 : 1*2^40*2^31*2^20*2^11*2^0&#xff0c; 那么请你编程实现&#xff0c;将一个M进制的数N转换成十进制表示…
最新文章