MySQL中的Join连接查询

news2024/11/20 10:24:50

目录

    • Join
    • Join的分类
    • 笛卡尔积
      • 笛卡尔积出现的原因
      • 为什么不推荐有笛卡尔积出现
      • 那应该怎么做多表连接
    • Join的使用
    • 小表驱动大表
      • 小表驱动大表是什么
      • 小表驱动大表的好处
      • 如何区分哪一个是驱动表和被驱动表
    • Join原理及算法
    • NLJ算法
    • BNLJ算法
    • 总结:如何写入高性能的连接查询
    • 为什么MySQL不推荐使用Join
    • 那有什么可以替代Join的方案

Join

首先我们先抛出一个问题,为什么要使用Join?
Join可以将我们数据库中的两张或者两张以上的表进行连接操作,并且使用Join做关联查询的好处是可以做分页。
 
 

Join的分类

Join的分类我们可以具体看下面这张图片。

 
在这里插入图片描述
 
具体分类说明看下文。
 
 

笛卡尔积

笛卡尔积是我们多表联合查询的时候会出现的一种现象。我们来举个例子来简单理解笛卡尔积。
假设集合A={a, b}有两个元素,集合B={0, 1, 2}有三个元素,则两个集合的笛卡尔积为
{(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)},有2*3为6个元素。
 

笛卡尔积出现的原因

  • 如果数据库表关联查询时,只是纯粹的进行表连接没有使用其他条件,会出现全部的笛卡尔积。
  • 数据库表关联查询,如果ON条件是非唯一字段,则会出现局部笛卡尔积。
  • 数据库表关联查询,如果ON条件是表的唯一字段,则不会出现笛卡尔积。
     

为什么不推荐有笛卡尔积出现

  • 笛卡尔积没有什么实践意义。在实际应用中,笛卡尔积本身大多没有什么实际用处,只有在两个表连接时加上限制条件,才会有实际意义。
  • 多个大数据量的表产生的笛卡尔积表会占用很大的内存空间。
     

那应该怎么做多表连接

我们在进行表连接查询的时候一般都会使用JOIN xxx ON xxx的语法,ON语句的执行是在JOIN语句之前的,也就是说两张表数据行之间进行匹配的时候,会先判断数据行是否符合ON语句后面的条件,再决定是否JOIN。

(因此,有一个显而易见的SQL优化的方案是,当两张表的数据量比较大,又需要连接查询时,应该使用 FROM table1 JOIN table2 ON xxx的语法,避免使用 FROM table1,table2 WHERE xxx 的语法,因为后者会在内存中先生成一张数据量比较大的笛卡尔积表,增加了内存的开销。)

 
 

Join的使用

Join的分类

  1. 内联接(Inner Join):内连接查询返回满足条件的所有记录,如果没有指定是left还是right 仅仅只有一个join也是inner join。
  2. 左联接(Left Join):除了匹配2张表中相关联的记录外,还会匹配左表中剩余的记录,右表中未匹配到的字段用NULL表示。
  3. 右外联接(Right Join):除了匹配2张表中相关联的记录外,还会匹配右表中剩余的记录,左表中未匹配到的字段用NULL表示。

 
 

小表驱动大表

说到连接,我们经常会听到的规则是小表驱动大表

 
 

小表驱动大表是什么

小表驱动大表指的是用小数据集的来驱动大数据集。
 
 

小表驱动大表的好处

我们可以使用两个循环来理解小表驱动大表,例如:现有两个表A与B ,表A有200条数据,表B有20万条数据
小表驱动大表 : A驱动表,B被驱动表

 for(200条){
     for(20万条){
       ...
     }
 }

大表驱动小表 : B驱动表,A被驱动表

 for(20万){
      for(200条){
       ...
      }
 }

看以上两个for循环,总共循环的次数是一样的。但是对于MySQL数据库而言,并不是这样了,我们尽量选择第1个for循环,也就是小表驱动大表。
数据库连接的建立,第一个建立了200条次链接,第二个建立了20万次链接。假设链接了两次,每次做上百万次的数据集查询,查完就走,这样就只做了两次;相反建立了上百万次链接,申请链接释放反复重复,这样系统就受不了了。

综上,小表驱动大表的好处是

  • 通过减少创建连接的次数,来加快查询速度。
  • 驱动表索引没有生效,被驱动表索引有效 。检索大表的时候可以使用索引,B+树查找时间复杂度是logn,所以小驱大大概的时间200log2000000,当然比2000000log200快很多
     
     

如何区分哪一个是驱动表和被驱动表

1)通过EXPLAIN查看SQL语句的执行计划可以判断在谁是驱动表,EXPLAIN语句分析出来的第一行的表即是驱动表 ;
2)在JOIN查询中经常用到的 inner join、left join、right join
(1)当使用left join时,左表是驱动表,右表是被驱动表 ;
(2)当使用right join时,右表时驱动表,左表是被驱动表 ;
(3)当使用inner join时,mysql会选择数据量比较小的表作为驱动表,大表作为被驱动表 ;

测试环境配置:MYSQL 5.7
数据准备:创建两张测试表 大表 user_big_info ,测试数据400万条, 小表user_small_info ,测试数据200万条 ;

CREATE TABLE `user_small_info` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `user_id` varchar(32) NOT NULL COMMENT '用户唯一标识',
  `username` varchar(32) NOT NULL DEFAULT '' COMMENT '用户名',
  `password` varchar(255) NOT NULL DEFAULT '' COMMENT '密码',
  `real_name` varchar(32) NOT NULL DEFAULT '' COMMENT '真实姓名',
  `phone` varchar(32) NOT NULL DEFAULT '' COMMENT '手机号码',
  `remarks` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态 1-启用 2-禁用 ',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_user_id` (`user_id`) USING BTREE,
  KEY `idx_username` (`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3000001 DEFAULT CHARSET=utf8 COMMENT='用户表';


LEFT JOIN 测试:小表驱动大表

 
在这里插入图片描述

 

结果:

 
在这里插入图片描述
 
执行时间:18.141s ,由于使用左连接以小表为主表所以,返回行数:200万。
执行计划
 
在这里插入图片描述
 

LEFT JOIN 测试:大表驱动小表

 

在这里插入图片描述

 在这里插入图片描述
 
执行时间:25.949s ,由于使用左连接以大表为主表所以,返回行数: 400万
执行计划
 
在这里插入图片描述

 
 

Join原理及算法

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

在这里插入图片描述
 
 

NLJ算法

NLJ:Nested-Loop Join(嵌套循环算法)
执行过程:一次一行地从驱动表中读取行,在这行数据中取到关联字段,根据关联字段在被驱动表里取出满足条件的行,然后取出两张表的结果合集。

 EXPLAIN select * from t1 inner join t2 on t1.a= t2.a;

 
在这里插入图片描述
 

如果被驱动表中关联字段存在索引,整个过程会读取驱动表中所有的数据(比如100行),然后遍历每行数据中字段a的值,然后在遍历出来的a值索引扫描被驱动表中的对应行,一次找到一个,整个过程扫描了200行。

 
 

BNLJ算法

BNLJ:Block Nested-Loop Join(基于块的嵌套循环连接)
执行过程:把驱动表的数据全部读入join_buffer中,然后扫描被驱动表,把被驱动表的每一行取出来和join_buffer中的数据做对比,重复这个步骤。这个过程做了两次全表扫描ALL。

EXPLAIN select * from t1 inner join t2 on t1.b= t2.b;

 

在这里插入图片描述
 

整个过程对驱动表和被驱动表都做了一次全表扫描(比如t1=10000,t2=100),扫描总行数:10000+100=10100,并且join_buffer里面的数据是无序的,因此对被驱动表的每一行,都需要,100次判断,所以内存中的判断次数是10000*100=100w次。

 

问题1:join buffer一次性放不下t2表怎么办?
join_buffer 的大小是由参数 join_buffer_size 设定的,默认值是 256k。如果放不下表 t2 的所有数据话,策略很简单, 就是分段放。
举栗子:比如 t2 表有1000行记录, join_buffer 一次只能放800行数据,那么执行过程就是先往 join_buffer 里放800行记录,然 后从 t1 表里取数据跟 join_buffer 中数据对比得到部分结果,然后清空 join_buffer ,再放入 t2 表剩余200行记录,再 次从 t1 表里取数据跟 join_buffer 中数据对比。所以就多扫了一次 t1 表。
 

问题2:被驱动表的关联字段没索引为什么要选择使用 BNL 算法而不使用 Nested-Loop Join 呢?

如果上面第二条sql使用 Nested-Loop Join,那么扫描行数为 100 * 10000 = 100万次,这个是磁盘扫描。 很显然,用BNL磁盘扫描次数少很多,相比于磁盘扫描,BNL的内存计算会快得多。 因此MySQL对于被驱动表的关联字段没索引的关联查询,一般都会使用 BNL 算法。如果有索引一般选择 NLJ 算法,有 索引的情况下 NLJ 算法比 BNL算法性能更高。

 
 

总结:如何写入高性能的连接查询

  连表的时候,我们需要去关注磁盘的IO。一般的操作是用小表驱动大表,注意在连接字段上建立索引,特别是被驱动表,这样MySQL内部使用的就是NLJ算法进行连接处理,能有效减少磁盘IO。如果连接条件有索引,MySQL内部会使用NLJ算法。如果没有索引,MySQL内部会使用BNLJ算法来基于内存计算,减少磁盘的IO扫描。

 
 

为什么MySQL不推荐使用Join

在阿里巴巴的开发规范中有明确规定超过三个表的情况下禁止使用join,即使是两表join时,也需要注意表的索引,SQL性能。

所以,不推荐使用Join的原因是性能原因。
当表的到达百万级数据量后,Join导致DB性能下降。
分布式的分库分表,不建议使用Join,跨库join表现不良。
当系统比较大时,如果要修改表的字段,单表查询的修改比较容易,Join写的SQL语句也需要修改,单不容易发现。
 
 

那有什么可以替代Join的方案

  建议分别根据索引进行单表查询,单表查询出来之后,作为条件给下一次的单表查询。这样的结果我们更容易接受。

 

 
 

参考:
由笛卡尔积现象分析数据库表的连接
数据库开发应知应会之笛卡尔积
小表驱动大表
Mysql-表连接join中的NLJ、BNL算法

  
  
  

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

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

相关文章

一阶低通滤波介绍及simulink模型

一阶低通滤波 背景介绍 低通滤波是一种过滤方式,规定低频信号能正常通过,而超过设定临界值的高频信号则被阻隔、减弱。低通滤波可以简单的认为:设定一个频率点,当信号频率高于这个频率时不能通过,在数字信号中&#…

对象图实例解析

总目录链接>> AutoSAR入门和实战系列总目录 文章目录更快、更好、更轻松地学习 UML对象图的目的对象图一览类到对象图示例 - 订单系统基本对象图符号和符号类图与对象图对象图 - 通过示例学习对象图示例 I - 公司结构对象图示例 II - POS对象图示例 III - Writer对象结构…

Java中的Comparator 与 Comparable详解

Comparator VS Comparable1. Comparator1.1 对一维数组进行排序1.2 对二维数组进行排序1.3 对对象数组进行排序2. Comparable3. 二者区别1. Comparator 通过源码发现Comparator是一个接口。 根据compare方法中的注释可以发现方法返回三种类型的值,正数、零、负数&a…

4.1 路由器(华硕 官改/梅林 华为 小米 路由) 使用花生壳 实现远程管理

最近添置了一台华硕的八爪鱼GT AC5300,到手后刷了官改,而里面软件中就提供了花生壳程序,想到花生壳为每个用户提供了两条免费映射(带宽为1mbs,流量为1g/月),所以就打算利用来做一个远程访问。具…

开发手册——一、编程规约_7.控制语句

这篇文章主要梳理了在java的实际开发过程中的编程规范问题。本篇文章主要借鉴于《阿里巴巴java开发手册终极版》 下面我们一起来看一下吧。 1. 【强制】在一个 switch 块内,每个 case 要么通过 break / return 等来终止,要么注释说明程序将继续执行到哪…

CSGO服务器配置全贴纸插件方法教程

CSGO服务器配置全贴纸插件方法教程 关于插件的警告 一定要了解V社对于CSGO社区服务器的规定,全皮肤插件/全手套插件等,在设置了GSLT的情况下,是有可能被封禁GSLT账号的(所以慎用) 配置好服务器之后呢,想安…

uniapp+uView2.0实现自定义动态tabbar

1.需求说明 2.实现原理说明 3.实现过程 3.1集成uView2.0 3.2 自定义tabbar 3.3 vuex定义tabbar共享信息 3.4 tabbar显示个数控制 1.需求说明 要求不同时间显示不同的tabbar.点击不同的tabbar跳转到不同的页面,能随时…

【2021.9.7】记一次exe手动添加shellcode

【2021.9.7】记一次exe手动添加shellcode 文章目录【2021.9.7】记一次exe手动添加shellcode0.大致思路1.获取MessageBox的真实地址VA2.通过OD在代码段添加shellcode3.dump出数据,设置程序OEP4.测试dump出来的exe5.方法总结测试的exe和添加了shellcode的exe:链接&…

【论文简述】PVSNet: Pixelwise Visibility-Aware Multi-ViewStereo Network(arxiv 2020)

一、论文简述 1. 第一作者:Qingshan Xu 2. 发表年份:2020 3. 发表期刊:arxiv 4. 关键词:MVS、3D重建、可见性、代价体、训练策略 5. 探索动机:ETH3D基准测试提供的图像包含强烈的视图变化,这就要求MVS…

刷题笔记4 | 24. 两两交换链表中的节点、19. 删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II

24. 两两交换链表中的节点 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。 输入:head [1,2,3,4] 输出&#xff1a…

JDBC概述二(JDBC编程+案例展示)

一(JDBC的编程步骤) 1.加载数据库驱动 加载数据库驱动通常使用class类的静态方法forName()来实现,具体实现方式如下: Class.forName(“DriverName”),DriverName就是数…

RuoYi-Flowable-Plus(代码生成)

RuoYi-Flowable-Plus搭建 若依所有扩展项目的代码生成功能都是一样的&#xff0c;RuoYi-Flowable-Plus为例来演示。 模块创建 1.创建新模块ruoyi-student2.编辑RuoYi-Flowable-Plus\pom.xml <dependency><groupId>com.ruoyi</groupId><artifactId>ruoy…

搭建Java环境

使用Java语言开发首先必须搭建好开发环境。 以windows 10为例&#xff0c;进行Java环境搭建分为以下几个步骤 1、下载并安装JDK 2、配置环境变量 1、下载并安装JDK 可以访问网站http://www.oracle.com/index.html进行SDK的下载&#xff08;因网站经常改版&#xff0c;这里就…

python 操作word库docx 增强接口

前言用python 的docx 库操作word完成一些自动化的文档生成工作&#xff0c;但有时候会遇到docx库提供的操作无法直接满足业务上的需求&#xff0c;需要对其进行一些扩展。接口完善实现在指定的文字后面插入指定的文字任务&#xff1a;以下示例需要在文档中的所有 "人生苦短…

Swing进度条演示(传输真实文件)

目录 GIF演示 代码 此示例涵盖的知识点&#xff1a;线程、IO流、File、Swing、Listener、JFrame、JFileDialog、JOptionPane、JProgressBar、Timer GIF演示 代码 package psn.exer.progress;import javax.swing.*; import java.awt.*; import java.io.*; import java.util.U…

【PyTorch】P1 简介

PyTorch 基础PyTorch 简介机器学习框架PyTorch 与 TensorFlow 的核心之争PyTorch生态PyTorch能做什么开发环境选择Pytorch Cuda 安装与疑难解答PyTorch 简介 2002年提出 torch 框架&#xff0c;是通用的机器学习计算框架&#xff0c;支持GPU加速运算&#xff1b; 2011年推出 to…

k-Tree(DP)

k-Tree1、问题2、思路&#xff08;DP&#xff09;3、代码1、问题 2、思路&#xff08;DP&#xff09; 这道题翻译过来就是说&#xff0c;给我们一个k叉树&#xff0c;然后每个点到子节点的边的边权从左到右依次为1到k。然后我们从根节点出发&#xff0c;向下走&#xff0c;我们…

@RequestParam和@PathVariable的用法与区别

PathVariable PathVariable 映射 URL 绑定的占位符带占位符的 URL 是 Spring3.0 新增的功能&#xff0c;该功能在SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义通过 PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中&#xff1a;URL 中的 {xxx} 占…

[vue] Vite的使用

[vue] Vite的使用环境package.json文件变更变更脚手架修改脚本命令index.html 文件htmlWebpackPlugin 替换为 vite-plugin-htmlWere sorry but vue3.x-vite-ts doesnt work properly without JavaScript enabled. Please enable it to continue.vite.config.ts 文件变更vite-pl…

设计模式之行为型模式

四、行为型模式 行为型模式用于描述程序在运行时复杂的流程控制&#xff0c;即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务&#xff0c;它涉及算法与对象间职责的分配。 行为型模式分为类行为模式和对象行为模式&#xff0c;前者采用继承机制来在…