连接的原理(待修改)

搞数据库⼀个避不开的概念就是Join,翻译成中⽂就是连接。

相信很多⼩伙伴在初学连接的时候有些⼀脸懵逼,理解了连接的语义之后⼜可能不明⽩各个表中的记 录到底是怎么连起来的,以⾄于在使⽤的时候常常陷⼊下边两种误区:

误区⼀:业务⾄上,管他三七⼆⼗⼀,再复杂的查询也⽤在⼀个连接语句中搞定。

误区⼆:敬⽽远之,上次 DBA 那给报过来的慢查询就是因为使⽤了连接导致的,以后再也不敢⽤了。

连接简介 连接的本质 为了故事的顺利发展,我们先建⽴两个简单的表并给它们填充⼀点数据:

mysql> CREATE TABLE t1 (m1 int, n1 char(1));

Query OK, 0 rows affected (0.02 sec)

mysql> CREATE TABLE t2 (m2 int, n2 char(1));

Query OK, 0 rows affected (0.02 sec)

mysql> INSERT INTO t1 VALUES(1, 'a'), (2, 'b'), (3, 'c');

Query OK, 3 rows affected (0.00 sec) Records: 3  Duplicates: 0  Warnings: 0

mysql> INSERT INTO t2 VALUES(2, 'b'), (3, 'c'), (4, 'd');

Query OK, 3 rows affected (0.00 sec) Records: 3  Duplicates: 0  Warnings: 0

我们成功建⽴了t1、t2两个表,这两个表都有两个列,⼀个是INT类型的,⼀个是CHAR(1)类型的,填充好数据的两个表⻓这样:

mysql> SELECT * FROM t1;

+------+------+

| m1   | n1   |

+------+------+

|    1 | a    |

|    2 | b    |

|    3 | c    |

+------+------+

3 rows inset (0.00 sec)

mysql> SELECT * FROM t2;

+------+------+

| m2   | n2   |

+------+------+

|    2 | b    |

|    3 | c    |

|    4 | d    |

+------+------+

3 rows inset (0.00 sec)

连接的本质就是把各个连接表中的记录都取出来依次匹配的组合加⼊结果集并返回给⽤户。所以我们把t1和t2两个表连接起来的过程如下图所示:

这个过程看起来就是把t1表的记录和t2的记录连起来组成新的更⼤的记录,所以这个查询过程称之为连接查询。连接查询的结果集中包含⼀个表中的每⼀条记录 与另⼀个表中的每⼀条记录相互匹配的组合,像这样的结果集就可以称之为笛卡尔积。因为表t1中有3条记录,表t2中也有3条记录,所以这两个表连接之后的笛卡 尔积就有3×3=9⾏记录。在MySQL中,连接查询的语法也很随意,只要在FROM语句后边跟多个表名就好了,⽐如我们把t1表和t2表连接起来的查询语句可以写成这 样:

 

连接过程简介

如果我们乐意,我们可以连接任意数量张表,但是如果没有任何限制条件的话,这些表连接起来产⽣的笛卡尔积可能是⾮常巨⼤的。⽐⽅说3个100⾏记录的表连接 起来产⽣的笛卡尔积就有100×100×100=1000000⾏数据!所以在连接的时候过滤掉特定记录组合是有必要的,在连接查询中的过滤条件可以分成两种:

涉及单表的条件

这种只设计单表的过滤条件我们之前都提到过⼀万遍了,我们之前也⼀直称为搜索条件,⽐如t1.m1 > 1是只针对t1表的过滤条件,t2.n2 < 'd'是只针对t2表 的过滤条件。

涉及两表的条件

这种过滤条件我们之前没⻅过,⽐如t1.m1 = t2.m2、t1.n1 > t2.n2等,这些条件中涉及到了两个表,我们稍后会仔细分析这种过滤条件是如何使⽤的哈。 下边我们就要看⼀下携带过滤条件的连接查询的⼤致执⾏过程了,⽐⽅说下边这个查询语句

SELECT * FROM t1, t2 WHERE t1.m1 > 1 AND t1.m1 = t2.m2 AND t2.n2 < 'd';

在这个查询中我们指明了这三个过滤条件:

t1.m1 > 1

t1.m1 = t2.m2

t2.n2 < 'd'

那么这个连接查询的⼤致执⾏过程如下:

1. ⾸先确定第⼀个需要查询的表,这个表称之为驱动表。怎样在单表中执⾏查询语句我们在前⼀章都唠叨过了,只需要选取代价最⼩的那种访问⽅法去执⾏单表 查询语句就好了(就是说从const、ref、ref_or_null、range、index、all这些执⾏⽅法中选取代价最⼩的去执⾏查询)。此处假设使⽤t1作为驱动表,那么就 需要到t1表中找满⾜t1.m1 > 1的记录,因为表中的数据太少,我们也没在表上建⽴⼆级索引,所以此处查询t1表的访问⽅法就设定为all吧,也就是采⽤全 表扫描的⽅式执⾏单表查询。关于如何提升连接查询的性能我们之后再说,现在先把基本概念捋清楚哈。所以查询过程就如下图所示:

我们可以看到,t1表中符合t1.m1 > 1的记录有两条。

2. 针对上⼀步骤中从驱动表产⽣的结果集中的每⼀条记录,分别需要到t2表中查找匹配的记录,所谓匹配的记录,指的是符合过滤条件的记录。因为是根据 t1表中的记录去找t2表中的记录,所以t2表也可以被称之为被驱动表。上⼀步骤从驱动表中得到了2条记录,所以需要查询2次t2表。此时涉及两个表的 列的过滤条件t1.m1 = t2.m2就派上⽤场了:

当t1.m1 = 2时,过滤条件t1.m1 = t2.m2就相当于t2.m2 = 2,所以此时t2表相当于有了t2.m2 = 2、t2.n2 < 'd'这两个过滤条件,然后到t2表中 执⾏单表查询。

当t1.m1 = 3时,过滤条件t1.m1 = t2.m2就相当于t2.m2 = 3,所以此时t2表相当于有了t2.m2 = 3、t2.n2 < 'd'这两个过滤条件,然后到t2表中 执⾏单表查询。

所以整个连接查询的执⾏过程就如下图所示: 

 

 从上边两个步骤可以看出来,我们上边唠叨的这个两表连接查询共需要查询1次t1表,2次t2表。当然这是在特定的过滤条件下的结果,如果我们把t1.m1 > 1 这个条件去掉,那么从t1表中查出的记录就有3条,就需要查询3次t2表了。也就是说在两表连接查询中,驱动表只需要访问⼀次,被驱动表可能被访问多 次。

内连接和外连接

为了⼤家更好理解后边内容,我们先创建两个有现实意义的表,

CREATE TABLE student (    

number INT NOT NULL AUTO_INCREMENT COMMENT '学号',    

name VARCHAR(5) COMMENT '姓名',    

major VARCHAR(30) COMMENT '专业',    

PRIMARY KEY (number) ) Engine=InnoDB CHARSET=utf8 COMMENT '学⽣信息表';

CREATE TABLE score (    

number INT COMMENT '学号',    

subject VARCHAR(30) COMMENT '科⽬',    

score TINYINT COMMENT '成绩',    

PRIMARY KEY (number, score) ) Engine=InnoDB CHARSET=utf8 COMMENT '学⽣成绩表';

我们新建了⼀个学⽣信息表,⼀个学⽣成绩表,然后我们向上述两个表中插⼊⼀些数据,为节省篇幅,具体插⼊过程就不唠叨了,插⼊后两表中的数据如下:

 

 现在我们想把每个学⽣的考试成绩都查询出来就需要进⾏两表连接了(因为score中没有姓名信息,所以不能单纯只查询score表)。连接过程就是从student 表中取出记录,在score表中查找number相同的成绩记录,所以过滤条件就是student.number = socre.number,整个查询语句就是这样:

mysql> SELECT * FROM student, score WHERE student.number = score.number;

 

 字段有点多哦,我们少查询⼏个字段:

 从上述查询结果中我们可以看到,各个同学对应的各科成绩就都被查出来了,可是有个问题,史珍⾹同学,也就是学号为20180103的同学因为某些原因没有参 加考试,所以在score表中没有对应的成绩记录。那如果⽼师想查看所有同学的考试成绩,即使是缺考的同学也应该展示出来,但是到⽬前为⽌我们介绍的连 接查询是⽆法完成这样的需求的。我们稍微思考⼀下这个需求,其本质是想:驱动表中的记录即使在被驱动表中没有匹配的记录,也仍然需要加⼊到结果集。 为了解决这个问题,就有了内连接和外连接的概念:

对于内连接的两个表,驱动表中的记录在被驱动表中找不到匹配的记录,该记录不会加⼊到最后的结果集,我们上边提到的连接都是所谓的内连接。

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

在MySQL中,根据选取驱动表的不同,外连接仍然可以细分为2种:

左外连接

选取左侧的表为驱动表。

右外连接

选取右侧的表为驱动表。

可是这样仍然存在问题,即使对于外连接来说,有时候我们也并不想把驱动表的全部记录都加⼊到最后的结果集。这就犯难了,有时候匹配失败要加⼊结果 集,有时候⼜不要加⼊结果集,这咋办,有点⼉愁啊。。。噫,把过滤条件分为两种不就解决了这个问题了么,所以放在不同地⽅的过滤条件是有不同语义 的:

WHERE⼦句中的过滤条件

WHERE⼦句中的过滤条件就是我们平时⻅的那种,不论是内连接还是外连接,凡是不符合WHERE⼦句中的过滤条件的记录都不会被加⼊最后的结果集。

ON⼦句中的过滤条件

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

需要注意的是,这个ON⼦句是专⻔为外连接驱动表中的记录在被驱动表找不到匹配记录时应不应该把该记录加⼊结果集这个场景下提出的,所以如果把ON ⼦句放到内连接中,MySQL会把它和WHERE⼦句⼀样对待,也就是说:内连接中的WHERE⼦句和ON⼦句是等价的。

⼀般情况下,我们都把只涉及单表的过滤条件放到WHERE⼦句中,把涉及两表的过滤条件都放到ON⼦句中,我们也⼀般把放到ON⼦句中的过滤条件也称之为连 接条件。

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

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

相关文章

C++初阶 类和对象(上)

前言&#xff1a;C初阶系列&#xff0c;每一期博主都会使用简单朴素的语言将对应的知识分享给大家&#xff0c;争取让所有人都可以听懂&#xff0c;C初阶系列会持续更新&#xff0c;上学期间将不定时更新&#xff0c;但总会更的 目录 一、什么是面向对象编程 二、什么是类和如…

Python 十五个炫酷代码

1 炫酷彩虹 视频是动态的&#xff0c;不信你运行试试。 from turtle import *colors ["red", "yellow", "blue", "lime"] bgcolor("black")x 6 t [Turtle(), Turtle()]for index, i in enumerate(t):i.speed(0)i.color(&…

【排序算法】希尔排序(C语言)

【排序算法】—— 希尔排序 目录 一、希尔排序原理1. 插入排序的问题2. 希尔排序的思路 二、希尔排序的相关问题1. 为什么插入排序那么多但效率却很高2. 如何选择希尔增量 三、代码实现1. 代码实现思路2. 实现代码 希尔排序是对直接插入排序的优化&#xff0c;在学习之前&…

C++ 之命名空间namespace【详解】

文章目录 一&#xff0c;命名空间出现的意义二&#xff0c;命名空间的定义命名空间里面可以包含变量&#xff0c;函数&#xff0c;类型&#xff1a;命名空间不可以定义在局部作用域&#xff1a;命名空间可以嵌套&#xff1a; 三&#xff0c;同一个工程中允许存在多个相同名称的…

Axios 通过a标签下载文件 跨域下载

<!-- a标签占位 --><a ref"down" ></a>getTest() {this.$axios.request({url: https://cnv13.55.la/download?file_key3695fa9461a0ae59cf3148581e4fe339&handle_typeexcel2pdf,method: get,responseType: blob, // 切记类型 blob}).then(re…

语音识别技术在医疗行业中的应用案例

随着语音识别技术和计算机视觉技术的不断提高&#xff0c;现代医学正在进入全面数字化时代。 追求高质量的训练数据是人工智能产业的信条&#xff0c;得到更为精准的语音机器模型更离不开语音数据的不断供给。本文讲介绍: 什么是语音识别技术语音识别技术如何应用于医疗行业 …

shell 脚本的函数和数组

函数 —— 封装的一个公式&#xff1a;sin、cos、tan —— 函数为脚本的别名 —— 函数就是一个功能模块&#xff0c;在函数中写执行的命令即可&#xff1b;使用函数可以避免代码重复&#xff0c;增加可读性&#xff0c;简化脚本&#xff0c;使用函数可以将大的工程分割为若…

JSP过滤器和监听器

什么是过滤器 Servlet过滤器与Servlet十分相似&#xff0c;但它具有拦截客户端&#xff08;浏览器&#xff09;请求的功能&#xff0c;Servlet过滤器可以改变请求中的内容&#xff0c;来满足实际开发中的需要。 对于程序开发人员而言&#xff0c;过滤器实质就是在Web应用服务器…

九、ffmpeg命令转封装

开了几天小差&#xff0c;今天继续学习ffmpeg。 准备测试使用的视频&#xff0c;并查看其信息 # 查看视频信息。使用Mediainfo也可以 ffprobe test.mp4 视频格式的信息如下。 保持编码格式&#xff1a;ffmpeg -i test.mp4 -vcodec copy -acodec copy test_copy.tsffmpeg -i…

Unity地面交互效果——6、地形动态顶点置换和曲面细分

回到目录 Unity置换贴图局部距离曲面细分 大家好&#xff0c;我是阿赵。   这篇文章是我无聊的时候做了一个demo&#xff0c;觉得挺有趣&#xff0c;于是就发上来。这里面包含了4个内容&#xff1a;置换贴图、顶点偏移、局部曲面细分&#xff0c;曲面细分按距离调整强度。 …

Jmeter快速入门

文章目录 1.安装Jmeter1.1.下载1.2.解压1.3.运行 2.快速入门2.1.设置中文语言2.2.基本用法 1.安装Jmeter Jmeter依赖于JDK&#xff0c;所以必须确保当前计算机上已经安装了JDK&#xff0c;并且配置了环境变量。 1.1.下载 可以Apache Jmeter官网下载&#xff0c;地址&#xf…

Volcano3D绘制3D火山图

一边学习&#xff0c;一边总结&#xff0c;一边分享&#xff01; 本期教程内容 **注&#xff1a;**本教程详细内容 Volcano3D绘制3D火山图 一、前言 火山图是做差异分析中最常用到的图形&#xff0c;在前面的推文中&#xff0c;我们也推出了好几期火山图的绘制教程&#xff0…