MySql进阶:深入理解MySQL语句执行逻辑

news2024/12/25 10:44:55

深入理解MySQL语句执行逻辑

一、前言

本文源自微博客(www.microblog.store),且以获得授权
     一直是想知道一条SQL语句是怎么被执行的,它执行的顺序是怎样的,然后查看总结各方资料,就有了下面这一篇博文了。
  本文将从MySQL总体架构—>查询执行流程—>语句执行顺序来探讨一下其中的知识。

二、MySQL架构总览

     架构最好看图,再配上必要的文字说明。下图根据参考书籍中一图为原本,再在其上添加了自己的理解。

image.png

     从上图中我们可以看到,整个架构分为两层,上层是MySQLD的被称为的SQL Layer,下层是各种各样对上提供接口的存储引擎,被称为Storage Engine Layer。其它各个模块和组件,从名字上就可以简单了解到它们的作用,这里就不再累述了。

三、查询执行流程

     下面再向前走一些,容我根据自己的认识说一下查询执行的流程是怎样的:

3.1、连接

  1. 客户端发起一条Query请求,监听客户端的连接管理模块接收请求
  2. 将请求转发到连接进/线程模块
  3. 调用用户模块进行授权检查
  4. 通过检查后,连接进/线程模块线程连接池中取出空闲的被缓存的连接线程和客户端请求对接,如果失败则创建一个新的连接请求

3.2、处理

  1. 先查询缓存,检查Query语句是否完全匹配,接着再检查是否具有权限,都成功则直接取数据返回
  2. 上一步有失败则转交给命令解释器,经过语法分析,语法分析后生成解析树
  3. 接下来是预处理阶段,处理解析器无法解决的语义,检查权限等,生成新的解析树
  4. 再转交给对应的模块处理
  5. 如果是SELECT查询还会经由查询优化器做大量的优化,生成执行计划
  6. 模块收到请求后,通过访问控制模块检查所连接的用户是否有访问目标表和目标字段权限
  7. 有则调用表管理模块,先是查看table cache中是否存在,有则直接对应的表和获取锁,否则重新打开表文件
  8. 根据表的meta数据,获取表的存储引擎类型等信息,通过接口调用对应的存储引擎处理
  9. 上述过程中产生数据变化的时候,若打开日志功能,则会记录到相应二进制日志文件中

3.3、结果

  1. Query请求完成以后,将结果集返回给连接进/线程模块
  2. 返回的也可以是相应的状态标识,如成功或失败等
  3. 连接进/线程模块进行后续的清理工作,并继续等待请求或断开与客户端的连接

一图总结:

image.png

四、SQL解析顺序

     接下来再走一步,让我们看看一条SQL语句的前世今生。首先看一下实例语句:

SELECT DISTINCT
    < select_list >
FROM
    < left_table > < join_type >
JOIN < right_table > ON < join_condition >
WHERE
    < where_condition >
GROUP BY
    < group_by_list >
HAVING
    < having_condition >
ORDER BY
    < order_by_condition >
LIMIT < limit_number >

然而牠的执行顺序是这样的:

FROM <left_table>
ON <join_condition>
<join_type> JOIN <right_table>
WHERE <where_condition>
GROUP BY <group_by_list>
HAVING <having_condition>
SELECT
DISTINCT <select_list>
ORDER BY <order_by_condition>
LIMIT <limit_number>

     虽然自己没想到是这样的,不过一看还是很自然和谐的,从哪里获取,不断的过滤条件,要选择一样或不一样的,排好序,那才知道要取前几条呢。
既然如此了,那就让我们一步步来看看其中的细节吧。

4.1、准备工作

  1. 创建数据库
create database testQuery
  1. 创建测试表
CREATE TABLE table1
(
    uid VARCHAR(10) NOT NULL,
    name VARCHAR(10) NOT NULL,
    PRIMARY KEY(uid)
)ENGINE=INNODB DEFAULT CHARSET=UTF8;

CREATE TABLE table2
(
    oid INT NOT NULL auto_increment,
    uid VARCHAR(10),
    PRIMARY KEY(oid)
)ENGINE=INNODB DEFAULT CHARSET=UTF8;
  1. 插入数据
INSERT INTO table1(uid,name) VALUES('aaa','mike'),('bbb','jack'),('ccc','mike'),('ddd','mike');

INSERT INTO table2(uid) VALUES('aaa'),('aaa'),('bbb'),('bbb'),('bbb'),('ccc'),(NULL);
  1. 查询数据
SELECT
    a.uid,
    count(b.oid) AS total
FROM
    table1 AS a
LEFT JOIN table2 AS b ON a.uid = b.uid
WHERE
    a. NAME = 'mike'
GROUP BY
    a.uid
HAVING
    count(b.oid) < 2
ORDER BY
    total DESC
LIMIT 1;

现在开始SQL解析之旅吧!

4.2、FROM

     当涉及多个表的时候,左边表的输出会作为右边表的输入,之后会生成一个虚拟表VT1。

4.2.1、(1-J1)笛卡尔积

     计算两个相关联表的笛卡尔积(CROSS JOIN) ,生成虚拟表VT1-J1:

mysql> select * from table1,table2;
+-----+------+-----+------+
| uid | name | oid | uid  |
+-----+------+-----+------+
| aaa | mike |   1 | aaa  |
| bbb | jack |   1 | aaa  |
| ccc | mike |   1 | aaa  |
| ddd | mike |   1 | aaa  |
| aaa | mike |   2 | aaa  |
| bbb | jack |   2 | aaa  |
| ccc | mike |   2 | aaa  |
| ddd | mike |   2 | aaa  |
| aaa | mike |   3 | bbb  |
| bbb | jack |   3 | bbb  |
| ccc | mike |   3 | bbb  |
| ddd | mike |   3 | bbb  |
| aaa | mike |   4 | bbb  |
| bbb | jack |   4 | bbb  |
| ccc | mike |   4 | bbb  |
| ddd | mike |   4 | bbb  |
| aaa | mike |   5 | bbb  |
| bbb | jack |   5 | bbb  |
| ccc | mike |   5 | bbb  |
| ddd | mike |   5 | bbb  |
| aaa | mike |   6 | ccc  |
| bbb | jack |   6 | ccc  |
| ccc | mike |   6 | ccc  |
| ddd | mike |   6 | ccc  |
| aaa | mike |   7 | NULL |
| bbb | jack |   7 | NULL |
| ccc | mike |   7 | NULL |
| ddd | mike |   7 | NULL |
+-----+------+-----+------+
28 rows in set (0.00 sec)
4.2.2、(1-J2)ON过滤

     基于虚拟表VT1-J1这一个虚拟表进行过滤,过滤出所有满足ON 谓词条件的列,生成虚拟表VT1-J2。
     注意:这里因为语法限制,使用了’WHERE’代替,从中读者也可以感受到两者之间微妙的关系;

mysql> SELECT
    -> *
    -> FROM
    -> table1,
    -> table2
    -> WHERE
    -> table1.uid = table2.uid
    -> ;
+-----+------+-----+------+
| uid | name | oid | uid  |
+-----+------+-----+------+
| aaa | mike |   1 | aaa  |
| aaa | mike |   2 | aaa  |
| bbb | jack |   3 | bbb  |
| bbb | jack |   4 | bbb  |
| bbb | jack |   5 | bbb  |
| ccc | mike |   6 | ccc  |
+-----+------+-----+------+
6 rows in set (0.00 sec)
4.2.3、(1-J3)添加外部列

     如果使用了外连接(LEFT,RIGHT,FULL),主表(保留表)中的不符合ON条件的列也会被加入到VT1-J2中,作为外部行,生成虚拟表VT1-J3。

mysql> SELECT
    -> *
    -> FROM
    -> table1 AS a
    -> LEFT OUTER JOIN table2 AS b ON a.uid = b.uid;
+-----+------+------+------+
| uid | name | oid  | uid  |
+-----+------+------+------+
| aaa | mike |    1 | aaa  |
| aaa | mike |    2 | aaa  |
| bbb | jack |    3 | bbb  |
| bbb | jack |    4 | bbb  |
| bbb | jack |    5 | bbb  |
| ccc | mike |    6 | ccc  |
| ddd | mike | NULL | NULL |
+-----+------+------+------+
7 rows in set (0.00 sec)

下面从网上找到一张很形象的关于‘SQL JOINS’的解释图,如若侵犯了你的权益,请劳烦告知删除,谢谢。
image.png

4.3 WHERE

     对VT1过程中生成的临时表进行过滤,满足WHERE子句的列被插入到VT2表中。

  • 注意:
    此时因为分组,不能使用聚合运算;也不能使用SELECT中创建的别名;
  • 与ON的区别:
    如果有外部列,ON针对过滤的是关联表,主表(保留表)会返回所有的列;如果没有添加外部列,两者的效果是一样的;
  • 应用:
    对主表的过滤应该放在WHERE;
    对于关联表,先条件查询后连接则用ON,先连接后条件查询则用WHERE;
mysql> SELECT
    -> *
    -> FROM
    -> table1 AS a
    -> LEFT OUTER JOIN table2 AS b ON a.uid = b.uid
    -> WHERE
    -> a. NAME = 'mike';
+-----+------+------+------+
| uid | name | oid  | uid  |
+-----+------+------+------+
| aaa | mike |    1 | aaa  |
| aaa | mike |    2 | aaa  |
| ccc | mike |    6 | ccc  |
| ddd | mike | NULL | NULL |
+-----+------+------+------+
4 rows in set (0.00 sec)

4.4 GROUP BY

     这个子句会把VT2中生成的表按照GROUP BY中的列进行分组。生成VT3表。

  • 注意:
    其后处理过程的语句,如SELECT,HAVING,所用到的列必须包含在GROUP BY中,对于没有出现的,得用聚合函数;
  • 原因:
    GROUP BY改变了对表的引用,将其转换为新的引用方式,能够对其进行下一级逻辑操作的列会减少;
  • 我的理解是:
    根据分组字段,将具有相同分组字段的记录归并成一条记录,因为每一个分组只能返回一条记录,除非是被过滤掉了,而不在分组字段里面的字段可能会有多个值,多个值是无法放进一条记录的,所以必须通过聚合函数将这些具有多值的列转换成单值;
mysql> SELECT
    -> *
    -> FROM
    -> table1 AS a
    -> LEFT OUTER JOIN table2 AS b ON a.uid = b.uid
    -> WHERE
    -> a. NAME = 'mike'
    -> GROUP BY
    -> a.uid;
+-----+------+------+------+
| uid | name | oid  | uid  |
+-----+------+------+------+
| aaa | mike |    1 | aaa  |
| ccc | mike |    6 | ccc  |
| ddd | mike | NULL | NULL |
+-----+------+------+------+
3 rows in set (0.00 sec)

4.5 HAVING

     这个子句对VT3表中的不同的组进行过滤,只作用于分组后的数据,满足HAVING条件的子句被加入到VT4表中。

mysql> SELECT
    -> *
    -> FROM
    -> table1 AS a
    -> LEFT OUTER JOIN table2 AS b ON a.uid = b.uid
    -> WHERE
    -> a. NAME = 'mike'
    -> GROUP BY
    -> a.uid
    -> HAVING
    -> count(b.oid) < 2;
+-----+------+------+------+
| uid | name | oid  | uid  |
+-----+------+------+------+
| ccc | mike |    6 | ccc  |
| ddd | mike | NULL | NULL |
+-----+------+------+------+
2 rows in set (0.00 sec)

4.6 SELECT

     这个子句对SELECT子句中的元素进行处理,生成VT5表。

  • (5-J1)计算表达式 计算SELECT 子句中的表达式,生成VT5-J1
  • (5-J2)DISTINCT
    寻找VT5-1中的重复列,并删掉,生成VT5-J2

     如果在查询中指定了DISTINCT子句,则会创建一张内存临时表(如果内存放不下,就需要存放在硬盘了)。这张临时表的表结构和上一步产生的虚拟表VT5是一样的,不同的是对进行DISTINCT操作的列增加了一个唯一索引,以此来除重复数据。

mysql> SELECT
    -> a.uid,
    -> count(b.oid) AS total
    -> FROM
    -> table1 AS a
    -> LEFT OUTER JOIN table2 AS b ON a.uid = b.uid
    -> WHERE
    -> a. NAME = 'mike'
    -> GROUP BY
    -> a.uid
    -> HAVING
    -> count(b.oid) < 2;
+-----+-------+
| uid | total |
+-----+-------+
| ccc |     1 |
| ddd |     0 |
+-----+-------+
2 rows in set (0.00 sec)

4.7 ORDER BY

     从VT5-J2中的表中,根据ORDER BY 子句的条件对结果进行排序,生成VT6表。

注意:唯一可使用SELECT中别名的地方;

mysql> SELECT
    -> a.uid,
    -> count(b.oid) AS total
    -> FROM
    -> table1 AS a
    -> LEFT OUTER JOIN table2 AS b ON a.uid = b.uid
    -> WHERE
    -> a. NAME = 'mike'
    -> GROUP BY
    -> a.uid
    -> HAVING
    -> count(b.oid) < 2
    -> ORDER BY
    -> total DESC;
+-----+-------+
| uid | total |
+-----+-------+
| ccc |     1 |
| ddd |     0 |
+-----+-------+
2 rows in set (0.00 sec)

4.8 LIMIT

     LIMIT子句从上一步得到的VT6虚拟表中选出从指定位置开始的指定行数据。

注意:
offset和rows的正负带来的影响;
当偏移量很大时效率是很低的,可以这么做:
采用子查询的方式优化,在子查询里先从索引获取到最大id,然后倒序排,再取N行结果集
采用INNER JOIN优化,JOIN子句里也优先从索引获取ID列表,然后直接关联查询获得最终结果

mysql> SELECT
    -> a.uid,
    -> count(b.oid) AS total
    -> FROM
    -> table1 AS a
    -> LEFT JOIN table2 AS b ON a.uid = b.uid
    -> WHERE
    -> a. NAME = 'mike'
    -> GROUP BY
    -> a.uid
    -> HAVING
    -> count(b.oid) < 2
    -> ORDER BY
    -> total DESC
    -> LIMIT 1;
+-----+-------+
| uid | total |
+-----+-------+
| ccc |     1 |
+-----+-------+
1 row in set (0.00 sec)

至此SQL的解析之旅就结束了,上图总结一下:

image.png

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

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

相关文章

电商淘宝京东,精准关键词搜索API接口

当使用电商淘宝京东的精准关键词搜索API接口时&#xff0c;以下是清晰的步骤指南&#xff1a; 一、注册与申请API密钥 注册账号&#xff1a;在淘宝开放平台或京东开放平台注册成为开发者&#xff0c;并创建账号。 创建应用&#xff1a;登录后&#xff0c;在开放平台创建一个应…

运维技术栈总结

文章目录 Linux CommandBasecd/lschmod/chown/chgrpvi/vimscptarsudf Installrpmyumdeb/apt Filtertailgrepawkfindnetstatechotelnetwhereistouch/mkdirgzip/rar/tar Statistics Linux MonitorCPUtophtopsar Memoryfreevmstat I/Oiostatpidstatiotop Networknetstatiftoptcpdu…

网络编程3----TCP简单客户端服务器的实现

服务器的代码&#xff1a; package network;import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.security.spec.RSAOtherPrimeInfo; im…

前端可观测性系统建设

一. 背景 随着前端业务的日趋庞大&#xff0c;及时发现和解决业务中的问题、优化用户体验、实时监控业务健康度变得愈发重要。在业务层面&#xff0c;我们希望能够监控每次发布版本后&#xff0c;核心功能是否有显著提升或至少没有负面影响&#xff0c;核心接口是否正常运作&a…

禁用/屏蔽 Chrome 默认快捷键

Chrome 有一些内置的快捷键&#xff0c;但是它并没有像其他软件一样提供管理快捷键的界面。在某些时候&#xff0c;当我们因为个人需求希望禁用 Chrome 某些快捷键时&#xff0c;又无从下手。 好在有开发者开发了 Chrome 插件&#xff0c;可以禁用 Chrome 快捷键的插件&#x…

振动分析-4-振动传感器的安装部署

参考(电机、减速机、风机)振动传感器部署指南 设备状态监测的测点通常选在设备轴承或靠近轴承的位置&#xff0c;通过在轴向、垂直方向、水平方向部署振动传感器来实现设备振动信号的采集。但在实际工作中&#xff0c;考虑安装空间和硬件成本&#xff0c;部署过程通常被两个问题…

使用Apache Flink实现实时数据同步与清洗:MySQL和Oracle到目标MySQL的ETL流程

使用Apache Flink实现实时数据同步与清洗&#xff1a;MySQL和Oracle到目标MySQL的ETL流程 实现数据同步的ETL&#xff08;抽取、转换、加载&#xff09;过程通常涉及从源系统&#xff08;如数据库、消息队列或文件&#xff09;中抽取数据&#xff0c;进行必要的转换&#xff0c…

基于VTK9.3.0+Visual Studio2017 c++实现DICOM影像MPR多平面重建

开源库&#xff1a;VTK9.3.0 开发工具&#xff1a;Visual Studio2017 开发语言&#xff1a;C 实现过程&#xff1a; void initImageActor(double* Matrix, double* center, vtkSmartPointer<vtkImageCast> pImageCast,vtkSmartPointer<vtkImageReslice> imageRe…

Python 库PySpark,一个超级强大的数据处理引擎

目录 01初识 PySpark 为什么选择 PySpark? 安装 PySpark 配置 PySpark 02基本操作 创建 RDD 基本 RDD 操作 03DataFrame 和 Spark SQL 创建 DataFrame 基本 DataFrame 操作 使用 Spark SQL 04机器学习与流处理 …

MacOS - 3 招快速去除桌面上的图标文件

在平时用 Mac 电脑的时候&#xff0c;会产生许多我们不用的或废弃的图标、文件&#xff0c;在 Mac 桌面上显得很乱&#xff0c;不仅影响美观也直接影响了我们工作的心情。下面我们分享 3 招快速去除桌面上的图标或文件的方法&#xff0c;有需要的朋友可以试一试。 1. 右键删除&…

QPushButton、QCheckBox、QRadioPutton、QLineEdit用法

实现LineEdit 文本的 居左、居中、居右设置 实现LineEdit 文本的粗体、斜体、下划线设置 实现LineEdit 控件的 ReadOnly、Enable、ClearButtonEnable的设置 创建资源文件&#xff0c;引入button需要的icon 总体布局 窗体使用垂直布局&#xff0c;每个组合控件内部是水平布局 2个…

游泳耳机品牌排行榜,10大实力超群的游泳耳机分享!

在当今快节奏的生活中&#xff0c;运动已成为许多人不可或缺的一部分&#xff0c;不仅为了健康&#xff0c;也是释放压力、提升生活品质的有效方式。而随着科技与健身的深度融合&#xff0c;智能穿戴设备尤其是专为运动设计的耳机&#xff0c;正逐渐成为运动爱好者的新宠。对于…

nodejs爬虫小红书评论区

发现好像还是爬虫的知识热度比较高&#xff0c;最近一直在加强JS这块。这两天脚本模拟爬BOSS的时候也想着怎么用nodejs&#xff0c;昨天都没更新文章&#xff0c;Q-Q&#xff0c;因为一直failed没啥成果。 使用模块 这边可以看到使用的模块其实也挺多&#xff0c;但主要还是ht…

vue大作业-实现学校官网

vue大作业-实现学校官网 基于vue2实现的学校官网 项目展示 学校官网介绍 欢迎访问我们学校的官方网站&#xff0c;这里为您提供了全面的信息和资源&#xff0c;帮助您更好地了解我们的教育理念、教学资源和学术活动。 首页 首页是您了解我们学校的起点。这里展示了学校的最…

单元测试的思考与实践

1. 什么是单元测试 通常来说单元测试&#xff0c;是一种自动化测试&#xff0c;同时包含一下特性&#xff1a; 验证很小的一段代码&#xff08;业务意义 或者 代码逻辑 上不可再分割的单元&#xff09;&#xff0c;能够更准确的定位到问题代码的位置 能够快速运行&#xff08;…

初始化一个Android项目时,Android Studio会自动生成一些文件和目录结构,以帮助你快速上手开发

当你初始化一个Android项目时&#xff0c;Android Studio会自动生成一些文件和目录结构&#xff0c;以帮助你快速上手开发。这些文件和目录各自有其特定的功能和用途。下面我为你解释一下这些自动生成的内容&#xff1a; 1. app 目录 这是你的应用模块的根目录&#xff0c;包…

C++之模板(三)

1、缺省模板参数 可以将数据结构类型传递进来&#xff0c;比如vectop<T>&#xff08;如果没传就是默认&#xff09; 把vector当作类型参数来传递&#xff0c;从而使用它的接口然后适配出新的接口。实际上这个Stack称为适配器。有时候可能需要vector&#xff0c;但是又需…

深入解析知识付费平台的核心功能模块:满足个性化学习需求的数字化教育新星

在数字化学习的大潮中&#xff0c;知识付费平台已成为教育行业的一颗新星&#xff0c;它以满足用户需求为核心&#xff0c;提供便捷高效的学习渠道。该平台汇聚了各类专业知识&#xff0c;覆盖职业技能、生活兴趣和人文社科等多个领域&#xff0c;满足不同用户的学习需求。同时…

【二】【QT开发应用】QMake和CMake介绍,GN,QT三个窗口类的区别,QMainWindow, QWidget,QDialog

QMake和CMake介绍 qmake&#xff1a;qt独有的代码构建工具, 是一种简洁的构建工具&#xff0c;主要用于生成 Qt 项目的跨平台编译配置文件&#xff0c;语法简单&#xff0c;适合小型和中型项目。 cmake&#xff1a;C通用的代码构建工具&#xff0c;绝大部分C开源项目都使用cm…

MySQL 8.0 版本更新 要点 列表 (8.0-8.0.23)

开头还是介绍一下群&#xff0c;如果感兴趣 PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, Oceanbase, Sql Server 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;&#xff08;共 2370 人左右…