mysql(1)—— 详解一条sql语句的执行过程

本文深入解析了一条SQL语句在MySQL数据库中的执行流程,通过创建并查询一张学生表,详细阐述了FROM、WHERE、GROUP BY、HAVING、ORDER BY和LIMIT等子句的工作原理及执行顺序,帮助读者理解SQL查询的内部机制。

SQL是一套标准,全称结构化查询语言,是用来完成和数据库之间的通信的编程语言,SQL语言是脚本语言,直接运行在数据库上。同时,SQL语句与数据在数据库上的存储方式无关,只是不同的数据库对于同一条SQL语句的底层实现不同罢了,但结果相同。这有点类似于java中接口的作用,一个接口可以有不同的实现类,不同的实现类对于接口中方法的实现方式可以不同,结果可以相同。这里SQL语言的作用就类似于java中的接口,数据库就类似于java中接口的实现类,SQL语句就类似于java接口中的方法。不同的是java中接口的不同实现类对于接口中方法的执行结果可以相同,也可以不同,而不同的数据库对于同一条SQL语句的执行是相同的。(这里只是做一个类比,方便我们理解)

  一般情况下,大部分SQL语句在不同的数据库上是通用的,但我们知道每个数据库都有自己独有的特性,像在MySql数据库中,可以使用substr(取字符串),trim(去空格),ifnull(空值处理函数),还可以使用limit语句对数据库表进行截取,但这些都是oracle数据库没有的。(类比接口实现类中,实现类独有的方法,而接口中没有的)

  这里简单介绍一下mysql数据库,mysql数据库是一款关系型数据库,所谓关系型数据库就是以二维表的形式存储数据,使用行和列方便我们对数据的增删改查。

  这篇博客,我们以mysql数据库为例,对一条sql语句的执行流程进行分析。(本篇博客不涉及到表连接)

  首先,创建一张student表,字段有自增主键id,学生姓名name,学科subject,成绩grade

  建表语句:

复制代码

DROP TABLE IF EXISTS student;
CREATE TABLE `student` (
  `id` int(5) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) DEFAULT NULL,
  `subject` varchar(10) DEFAULT NULL,
  `grade` double(4,1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8;

复制代码

  初始化数据:

复制代码

INSERT INTO student(`name`,`subject`,grade)VALUES('aom','语文',88);
INSERT INTO student(`name`,`subject`,grade)VALUES('aom','数学',99);
INSERT INTO student(`name`,`subject`,grade)VALUES('aom','外语',55);

INSERT INTO student(`name`,`subject`,grade)VALUES('jack','语文',67);
INSERT INTO student(`name`,`subject`,grade)VALUES('jack','数学',44);
INSERT INTO student(`name`,`subject`,grade)VALUES('jack','外语',55);

INSERT INTO student(`name`,`subject`,grade)VALUES('susan','语文',56);
INSERT INTO student(`name`,`subject`,grade)VALUES('susan','数学',35);
INSERT INTO student(`name`,`subject`,grade)VALUES('susan','外语',77);

INSERT INTO student(`name`,`subject`,grade)VALUES('alice','语文',88);
INSERT INTO student(`name`,`subject`,grade)VALUES('alice','数学',77);
INSERT INTO student(`name`,`subject`,grade)VALUES('alice','外语',100);

INSERT INTO student(`name`,`subject`,grade)VALUES('rajo','语文',33);
INSERT INTO student(`name`,`subject`,grade)VALUES('rajo','数学',55);
INSERT INTO student(`name`,`subject`,grade)VALUES('rajo','外语',55);

复制代码

下面我们来看一下,数据在数据库中的存储形式。

  (图1.0)

现在针对这张student表中的数据提出一个问题:要求查询出挂科数目多于两门(包含两门)的前两名学生的姓名,如果挂科数目相同按学生姓名升序排列。

下面是这条查询的sql语句

SELECT `name`,COUNT(`name`) AS num FROM student WHERE grade < 60 GROUP BY `name` HAVING num >= 2 ORDER BY num DESC,`name` ASC LIMIT 0,2;

执行结果:

  图(1.1)

以上这条sql语句基本上概括了单表查询中所有要注意的点,那么我们就以这条sql为例来分析一下一条语句的执行流程。

1,一条查询的sql语句先执行的是 FROM student 负责把数据库的表文件加载到内存中去,如图1.0中所示。(mysql数据库在计算机上也是一个进程,cpu会给该进程分配一块内存空间,在计算机‘服务’中可以看到,该进程的状态)

  图(1.2)

2,WHERE grade < 60,会把(图1.0)所示表中的数据进行过滤,取出符合条件的记录行,生成一张临时表,如下图所示。

  图(1.3)

 

3,GROUP BY `name`会把图(1.3)的临时表切分成若干临时表,我们用下图来表示内存中这个切分的过程。

               

  图(1.4)              图(1.5)               图(1.6)              图(1.7)

4,SELECT 的执行读取规则分为sql语句中有无GROUP BY两种情况。

  (1)当没有GROUP BY时,SELECT 会根据后面的字段名称对内存中的一张临时表整列读取。

  (2)当查询sql中有GROUP BY时,会对内存中的若干临时表分别执行SELECT,而且只取各临时表中的第一条记录,然后再形成新的临时表。这就决定了查询sql使用GROUP BY的场景下,SELECT后面跟的一般是参与分组的字段和聚合函数,否则查询出的数据要是情况而定。另外聚合函数中的字段可以是表中的任意字段,需要注意的是聚合函数会自动忽略空值。

  我们还是以本例中的查询sql来分析,现在内存中有四张被GROUP BY `name`切分成的临时表,我们分别取名为 tempTable1,tempTable2,tempTable3,tempTable4分别对应图(1.4)、图(1.5)、图(1.6),图(1.7)下面写四条"伪SQL"来说明这个查询过程。

SELECT `name`,COUNT(`name`) AS num FROM tempTable1;
SELECT `name`,COUNT(`name`) AS num FROM tempTable2;
SELECT `name`,COUNT(`name`) AS num FROM tempTable3;
SELECT `name`,COUNT(`name`) AS num FROM tempTable4;

最后再次成新的临时表,如下图:

  图(1.8)

5,HAVING num >= 2对上图所示临时表中的数据再次过滤,与WHERE语句不同的是HAVING 用在GROUP BY之后,WHERE是对FROM student从数据库表文件加载到内存中的原生数据过滤,而HAVING 是对SELECT 语句执行之后的临时表中的数据过滤,所以说column AS otherName ,otherName这样的字段在WHERE后不能使用,但在HAVING 后可以使用。但HAVING的后使用的字段只能是SELECT 后的字段,SELECT后没有的字段HAVING之后不能使用。HAVING num >= 2语句执行之后生成一张临时表,如下:

   图(1.9)

6,ORDER BY num DESC,`name` ASC对以上的临时表按照num,name进行排序。

7,LIMIT 0,2取排序后的前两个。

以上就是一条sql的执行过程,同时我们在书写查询sql的时候应当遵守以下顺序。

SELECT XXX FROM XXX WHERE XXX GROUP BY XXX HAVING XXX ORDER BY XXX LIMIT XXX;
### SQL语句执行过程详解 SQL语句执行一个复杂的多阶段过程,在不同的数据库管理系统(如PostgreSQLMySQL、Oracle等)中,虽然具体实现细节有所不同,但总体逻辑基本一致。以下是SQL语句执行的主要阶段及其功能描述: #### 1. 客户端与服务器通信 当用户通过应用程序或命令行工具提交SQL语句时,客户端会将该请求发送至数据库服务器。这一阶段主要完成网络传输和协议交互的工作。 #### 2. 查询缓存(部分数据库支持) 某些数据库(如MySQL在8.0版本之前)会在接收到SQL语句后尝试匹配查询缓存。如果存在相同的查询结果,则直接返回缓存数据而无需进一步处理[^4]。然而需要注意的是,现代数据库逐渐弱化甚至移除了查询缓存机制,因为其维护成本较高且效果有限。 #### 3. SQL解析(Parsing) 在此阶段,数据库会对SQL语句进行语法分析和验证,确保其符合标准并能够被正确理解。这一步骤通常包括词法分析和语法分析两个子阶段: - **词法分析**:识别SQL字符串中的关键字、标识符、运算符等基本元素。 - **语法分析**:依据数据库定义的语言规则检查SQL结构是否合法。 例如对于`SELECT col_1, col_2 FROM Table_1 WHERE col_1 < 10;`这样的查询,解析器会确认表名`Table_1`是否存在以及列名`col_1`, `col_2`是否有效[^1]。 #### 4. 预处理 经过初步解析之后进入预处理阶段,此过程中数据库引擎将进一步校验对象权限、绑定变量值等内容,并构建内部表示形式以便后续操作使用[^2]。 #### 5. 查询重写与优化 为了提高性能,许多高级特性依赖于查询重写技术来简化原始表达式或者应用特定算法转换成更高效的替代方案;随后由查询优化器决定最佳策略以最小代价获得所需信息。常见的考虑因素包括索引利用情况、统计信息估计等等[^3]。 #### 6. 执行计划生成 基于前面所做的准备工作,此时会产生具体的物理执行路径——即所谓的“执行计划”。它明确了如何访问存储的数据文件、采用何种方式连接不同表格以及其他必要的计算步骤[^4]。 #### 7. 访问实际数据 按照既定规划开始真正触碰底层磁盘上的资料项,期间可能会涉及到锁定控制防止并发冲突等问题发生的同时读取/修改相应记录集[^1]。 #### 8. 结果组装与返回 最后收集所有满足条件的结果集合起来形成最终答复反馈给最初发起者那里去展示出来供查看用途所用[^2]。 ```sql -- 示例代码演示简单查询过程模拟 EXPLAIN ANALYZE SELECT col_1, col_2, col_3, col_4, col_5 FROM Table_1 JOIN Table_2 USING (col_1) WHERE col_1 < 10 GROUP BY col_3 HAVING SUM(col_4) > 1000 ORDER BY col_5 DESC LIMIT 10; ``` 上述脚本可用于观察整个流程的实际表现状况,借助`EXPLAIN ANALYZE`命令可以让开发者深入了解每一步耗费的时间资源分布详情。 --- 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值