40 mysql join 的实现

前言

join 是一个我们经常会使用到的一个 用法

我们这里 看一看各个场景下面的 join 的相关处理 

测试数据表如下, 两张测试表, tz_test, tz_test03, 表结构 一致

CREATE TABLE `tz_test` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`field1` varchar(128) DEFAULT NULL,`field2` varchar(128) DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE,UNIQUE KEY `field_1_2` (`field1`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8CREATE TABLE `tz_test_03` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`field1` varchar(128) DEFAULT NULL,`field2` varchar(128) DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE,UNIQUE KEY `field_1_2` (`field1`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8

 

tz_test 数据如下 

4a2ac7c294ea42dca097175e22fec09f.png

 

tz_test_03 数据如下 

952b3a865bf442d88b39137291a6e578.png

 

 

join条件基于主键

执行 sql 如下 ”select * from tz_test as t1 inner join tz_test_03 as t2 on t1.id = t2.id where 1 = 1”

sql 预处理的时候, 遍历各个 数据表, tz_test, tz_test_03, 将所有的字段列 merge 成为 all_fields, 传递个 qeb_tab->all_fields

这里是在迭代 tz_test, tz_test_03 的字段列表信息, 将其添加到 field_list

tz_test 的情况如下 

978ab5eb9b6d490591d58b1028d92c1d.png

 

tz_test_03 的情况如下 

03c03c5b2e664485b02e469594c3c5ed.png 

然后 field_list 传到后面的 JOIN

6b11ec18490641a28a49fda01235038e.png 

具体的 join 的实现 

91644a58fc42462fa8e08a1e4217e8e9.png 

外层循环, 基于主驱动表的循环, 这里是基于 普通的查询, 根据 主键索引, 普通索引, 全表扫描 去遍历记录, 可以看到 主驱动表 是 tz_test_03

这里填充的是 tz_test_03 的 field_list, 对应于 qep_tab->fields 中的 [3-5] 项

178e48fb25d34139a6120939e7721aa1.png

 

内层的循环是基于 外层的循环条件 来限定的查询, 假设 tz_test_03 拿到的记录为 "{"id":1, "field1":"field1", "field2":"2"}"

然后内层的循环的查询就是 "select * from tz_test where id = 1"

这里填充的是 tz_test 的 field_list, 对应于 qep_tab->fields 中的 [0-2] 项

4d457ce8d15f4ad796fd991e1bb34f38.png

 

因为是主键关联查询, 因此 info->read_record 是 join_no_more_records, qup_tab->reqd_first_record 会查询出 内层循环需要关联的 tz_test 中的记录 

ca879a63b1574c758efe596133e13823.png 

然后上面 外层循环填充了 qep_tab->fields 中的 [0-2] 项 

然后上面 内层循环填充了 qep_tab->fields 中的 [3-5] 项 

这里就是输出 qep_tab->fields 中的数据到客户端, 即为所求 

ddbf8d96c0d5466797394d400c3d691c.png

 

内层循环首次迭代基于 join_read_key 函数 

9c0acf7fa7a5430c83e5c4ec4b407ddf.png 

内层循环后续迭代基于 join_no_more_records 函数, 是直接响应 跳出 sub_select 循环

0e1930a942774203be849d860a391d45.png 

 

主驱动表的选择规则

选择的是 符合条件记录较少的表 作为主驱动表 

假设执行 sql 如下, 按照上述规则推导 会选择 tz_test_03 作为主驱动表 

select * from tz_test as t1

inner join tz_test_03 as t2 on t1.field1 = t2.field1

where t2.id = 2;

确实如此, 选择了 tz_test_03 作为主驱动表 

a571bae63e914ec883577e9ebc7b475f.png

 

假设执行 sql 如下, 按照上述规则推导 会选择 tz_test 作为主驱动表

select * from tz_test as t1

inner join tz_test_03 as t2 on t1.field1 = t2.field1

where t1.id = 2;

确实如此, 选择了 tz_test 作为主驱动表 

865607fec2a44367998a1ce84fe9cec2.png

 

 

join条件基于索引

执行 sql 如下 ”select * from tz_test as t1 inner join tz_test_03 as t2 on t1.field1 = t2.field1 where 1 = 1”

基于 field1 作为 join 条件处理类似 

只是, 这边 内层循环迭代的时候, 可能迭代多条记录, 这边是通过 内层循环表 的 field1 的索引进行迭代的 

d989b99106ea431f8bd06ec2ae97384b.png

 

内层循环首次迭代基于 join_read_always_key 函数  

60100d2fca7b4d1a9e517b4e112da71d.png 

内层循环后续迭代基于 ha_index_next_same 函数 

8afcb5e5cf5f49e191fe0344e35704a5.png 

 

join条件基于普通字段

执行 sql 如下 ”select * from tz_test as t1 inner join tz_test_03 as t2 on t1.field2 = t2.field2 where 1 = 1”

 

外层循环为 第一个 join->first_select, 走的普通的 sub_select 的流程, 基于 qep_tab->read_first_record 和 info->read_record 进行迭代 

迭代的过程中会将数据 放到 join_buffer, 主驱动表是 tz_test_03

fc1ff8c24deb4b74a9b959d3b603b8c3.png

 

基于 field2 作为 join 条件处理类似 

只是, 这里是基于 qep_tab->read_first_record 和 info->read_record 进行迭代, 然后 比较的是 join_buffer 中的 tz_test_03 表的数据 

0e135c8c7e6d432a8e0c246554906289.png

 

两层循环的比较处理是在这里, 这里 外层 while 遍历的是 tz_test 表的所有数据

for 中遍历的是 join_buffer 中的数据, 这里对应的是 tz_test_03 表中的所有数据 

然后 generate_full_extensions 中去做的是否符合 join 的条件, 以及输出 数据到客户端

ca005d7d29e54c27a11e6b532c1b7590.png

 

比如这里比较的是 tz_test 的第一条记录的 field2, 值为 “1”

和 join_buffer 中 tz_test_03 的第一条记录的 field2, 值为 “1”, 比较成功 

0fcfe4a8a8a54b9d89adb39f81af9a49.png

 

比如这里比较的是 tz_test 的第一条记录的 field2, 值为 “1”

和 join_buffer 中 tz_test_03 的第一条记录的 field2, 值为 “2”, 比较不成功 

37c6f01f5c1d45e1affe566f95f97bed.png

 

比较成功之后, 向客户端输出结果信息 

比较不成功, 迭代 下一条记录

84cc409b34a7416d855a352f4f92ce3e.png

 

 

join_buffer 和 fields 的数据是如何关联上的?

tz_test 中的数据, JOIN_CACHE_BNL::join_matching_records 的迭代中依次将记录信息填充到 qep_tab->read_record 中, 其实就是填充到 all_fields[0-2]

然后 我们看一下 join_buffer 和 fields 的数据是如何关联上的?

初始化的时候 我们看一下各个 fields 的信息

c1252d42430e49d782cc868922616137.png

 

向客户端输出结果信息的时候 各个 fields 的信息如下, 可以看到 和 初始化的时候是一致的, 是属于各个 Table 下面的各个 Field, buf 使用的是 table->record[0]

因此 上面 join_buffer 处理的时候讲数据记录填充到了 all_fields[3-5]

e151e6fc0220449d80319a4ce57cb817.png

 

join_buffer 中 field_descr 和 Table 中的 Field 关联是在 join_buffer 初始化的地方 

(*fld_ptr)->fill_cache_field 中会初始化 CACHE_FIELD 的 ptr, length, field 等等信息 

49e97ec5cb744e64ac080e836924ddf1.png

 

拷贝的地方如下, pos 为当前 join_buffer 读取到的位置, 读取之后 id 值为 2, field1 值为 ”field12”, field2 值为 2

这里会将 join_buffer 中的数据读取到 CACHE_FIELD 的各个列中, 对应的就是 Table 中的各个 Field, 也等价于填充到了 all_fields[3-5] 

cc750f45b7594f8eb46d26a4791b9f1f.png

 

其内容如下, 内容拆解如下, 主要是 四个部分, 这里对应的是记录 “{"id":"2", "field1":"field12", "field2":"2"}”

然后 紧接着是 id 为 4 的这一条记录, 再接着是 id 为 6 这条记录 

ed66d36132a242ae98cb5627f4104cc9.png

 

 

主驱动表的数据填充到 join_buffer 的地方在哪里?

主驱动表查询的每一条记录 都是直接往 join_buffer 中放 

这里显示的 qep_tab->table_ref->table_name 为 tz_test, 但是实际上查询的表示 tz_test_03

ac7a952714704c69a54665e53635ffdf.png

 

 

大数据表join条件基于主键是否有优化? 

构造 大表如下

45de28697cb14835955c8a846dfae71b.png

 

执行 sql 如下 “select * from tz_test as t1

inner join tz_test_03 as t2 on t1.id = t2.id

where 1 = 1;”

可以看到的是 查询实现是一样的, 主驱动表为的 tz_test_03, 然后 内层循环表为 tz_test

c6988e44d14040ed9375bd0dfb9d04bb.png

 

 

 

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BGP多跳及BGP4+

一、知识补充 1、BGP4 传统BGP-4只管理IPV4路由信息,对于使用其它网络程协议 (若IPV6等)的应用末给予支持。IETF对BGP-4扩展,提出BGP4,可以提供对IPV6、IPX和MPLS VPN的支持 (简单说: 扩展IPV6协议栈支持)。 2、全互联 在上一篇博文中提…

leetcode - 矩阵区域和

1314. 矩阵区域和 - 力扣&#xff08;LeetCode&#xff09; 给你一个 m x n 的矩阵 mat 和一个整数 k &#xff0c;请你返回一个矩阵 answer &#xff0c;其中每个 answer[i][j] 是所有满足下述条件的元素 mat[r][c] 的和&#xff1a; i - k < r < i k, j - k < c …

备忘录模式 rust和java的实现

文章目录 备忘录模式介绍实现javarustrust仓库 备忘录模式 备忘录&#xff08;Memento&#xff09;模式的定义&#xff1a;在不破坏封装性的前提下&#xff0c;捕获一个对象的内部状态&#xff0c;并在该对象之外保存这个状态&#xff0c;以便以后当需要时能将该对象恢复到原先…

【5G PHY】5G NR 如何计算资源块的数量?

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

严蔚敏数据结构p17(2.19)——p18(2.24) (c语言代码实现)

目录 2.19已知线性表中的元素以值递增有序排列,并以单链表作存储结构。试写一高效的算法,删除表中所有值大于 mink 且小于 maxk 的元素(若表中存在这样的元素&#xff09;同时释放被删结点空间,并分析你的算法的时间复杂度(注意:mink 和 maxk 是给定的个参变量,它们的值可以和表…

Hdoop学习笔记(HDP)-Part.12 安装HDFS

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …

继阿里云、滴滴、语雀后,腾讯视频也出现重大系统故障

昨晚&#xff0c;许多网友报告称腾讯视频出现了网络故障&#xff0c;具体表现为首页无法加载内容、VIP 用户无法观看会员视频等问题。 针对这一问题&#xff0c;腾讯视频回应称&#xff1a;目前腾讯视频遇到了暂时的技术问题&#xff0c;正在紧急修复中&#xff0c;各项功能正在…

在项目根目录未找到 app.json

这个问题就是我们在编译后的app.json文件找不到&#xff0c;路径出现了问题 首先看dist下我们该文件的路径 所以我们需要将该路径配置到我们project.config.json文件中去 在这里新加下面这行代码就可以了&#xff0c; "miniprogramRoot": "dist/dev/mp-weixi…

C语言扫雷游戏

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、扫雷游戏的分析和设计1.1扫雷游戏的功能说明1.2数据结构的分析1.3文件结构设计 二、扫雷游戏的代码实现总结 前言 详细介绍扫雷游戏的思路和实现过程。 一…

基于springboot + vue体育馆使用预约平台

qq&#xff08;2829419543&#xff09;获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;springboot 前端&#xff1a;采用vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xf…

自动提交日志脚本(6)浏览器抓包日志提交的数据

主要完成 do_logigdo_write_log 通过python的requests库post数据上传&#xff0c;因为是公司的系统我就展示抓包了&#xff0c;不展示怎么写了。 这边用日志暂存的页面做展示。 步骤 打开对应的页面&#xff0c;再打开浏览器的开发人员工具【一般是按f12】点击暂存按钮&…

每周一算法:背包问题(三)多重背包

多重背包 有 N N N件物品和一个容量是 M M M的背包。第 i i i种物品最多有 s i s_i si​件&#xff0c;每件的体积是 v i v_i vi​&#xff0c;价值是 w i w_i wi​。 求解将哪些物品装入背包&#xff0c;可使这些物品的总体积不超过背包容量&#xff0c;且总价值最大。 输…

最强Node js 后端框架学习看这一篇文章就够

距离上次认真花时间写作&#xff0c;似乎已经过了许久许久&#xff0c;前端讲了一个新框架 &#xff0c;叫 Nest.js 下方是课件&#xff0c;有过一定开发经验可跟随视频学习 B站 地址 &#xff1a; https://www.bilibili.com/video/BV1Lg4y197u1/?vd_sourcead427ffaf8a5c8344…

Linux --- 进程控制

目录 1. 进程创建 1.1. 内核数据结构的处理 1.2. 代码的处理 1.3. 数据的处理&#xff1a; 方案一&#xff1a;fork创建子进程的时候&#xff0c;直接对数据进行拷贝处理&#xff0c;让父子进程各自私有一份 方案二&#xff1a;写实拷贝(copy on write) 1.4. fork常规用…

后端Long型数据传到前端js后精度丢失的问题

假设一个场景&#xff0c;MybatisPlus的雪花算法生成long类型主键ID&#xff0c;存入数据库&#xff0c;前端获取到数据后&#xff0c;要执行一个更新操作&#xff08;updateById&#xff09;&#xff0c;但这时会出现无法成功更新的情况&#xff01;这是因为前端在长度大于17位…

24.Python 网络编程之socket编程

目录 1.认识TCP/IP2.socket编程2.1 使用socket2.2 使用socketserver 1.认识TCP/IP 计算机网络就是把各个计算机连接在一起&#xff0c;在网络中的计算机可以互相通信。 网络编程是如何在程序中实现两台计算机的通信。 网络通信是两台计算机上的两个进程之间的通信。 为了把…

【编码魔法师系列_构建型1.3 】抽象工厂模式(Abstract Factory)

学会设计模式&#xff0c;你就可以像拥有魔法一样&#xff0c;在开发过程中解决一些复杂的问题。设计模式是由经验丰富的开发者们&#xff08;GoF&#xff09;凝聚出来的最佳实践&#xff0c;可以提高代码的可读性、可维护性和可重用性&#xff0c;从而让我们的开发效率更高。通…

Linux常用命令——atq命令

在线Linux命令查询工具 atq 列出当前用户的at任务列表 补充说明 atq命令显示系统中待执行的任务列表&#xff0c;也就是列出当前用户的at任务列表。 语法 atq(选项)选项 -V&#xff1a;显示版本号&#xff1b; -q&#xff1a;查询指定队列的任务。实例 at now 10 minu…

一个完整的手工构建的cuda动态链接库工程 03记

1&#xff0c; 源代码 仅仅是加入了模板函数和对应的 .cuh文件&#xff0c;当前的目录结构如下&#xff1a; icmm/gpu/add.cu #include <stdio.h> #include <cuda_runtime.h>#include "inc/add.cuh"// different name in this level for different type…

pygame时序模块time

文章目录 简介时钟对象平抛运动 pygame系列&#xff1a;初步&#x1f48e;加载图像&#x1f48e;图像变换&#x1f48e;直线绘制 简介 之前在更新图形的时候&#xff0c;为了调控死循环的响应时间&#xff0c;用到了time.sleep。而实际上&#xff0c;我们并不需要额外导入其他…