SQL查询的底层运行原理深入分析

前言

SQL 语言无处不在。SQL 已经不仅仅是技术人员的专属技能了,似乎人人都会写SQL,就如同人人都是产品经理一样。如果你是做后台开发的,那么CRUD就是家常便饭。如果你是做数仓开发的,那么写SQL可能占据了你的大部分工作时间。我们在理解 SELECT 语法的时候,还需要了解 SELECT 执行时的底层原理。只有这样,才能让我们对 SQL 有更深刻的认识。本文分享将逐步分解SQL的执行过程,希望对你有所帮助。

数据准备

本文旨在说明SQL查询的执行过程,不会涉及太复杂的SQL操作,主要涉及两张表: citizen 和 city ,具体数据如下所示:

CREATE TABLE citizen (
 name CHAR ( 20 ),
 city_id INT ( 10 )
);

CREATE TABLE city (
 city_id INT ( 10 ),
 city_name CHAR ( 20 )
);

INSERT INTO city
VALUES
 ( 1, "上海" ),
 ( 2, "北京" ),
 ( 3, "杭州" );

INSERT INTO citizen
VALUES
("tom",3),
("jack",2),
("robin",1),
("jasper",3),
("kevin",1),
("rachel",2),
("trump",3),
("lilei",1),
("hanmeiei",1);

查询执行顺序

本文所涉及的查询语句如下,主要是citizen表与city表进行join,然后筛掉city_name != "上海"的数据,接着按照city_name进行分组,统计每个城市总人数大于2的城市,具体如下:

查询语句

SELECT
 city.city_name AS "City",
 COUNT(*) AS "citizen_cnt"
FROM citizen
 JOIN city ON citizen.city_id = city.city_id
WHERE city.city_name != '上海'
GROUP BY city.city_name
HAVING COUNT(*) >= 2
ORDER BY city.city_name ASC
LIMIT 2

执行步骤

上面SQL查询语句的书写书序是:

SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ...

但是执行顺序并不是这样,具体的执行顺序如下步骤所示:

  • 1.获取数据 ( From, Join )
  • 2.过滤数据 ( Where )
  • 3.分组 ( Group by )
  • 4.分组过滤 ( Having )
  • 5.返回查询字段 ( Select )
  • 6.排序与分页 ( Order by & Limit / Offset )

尖叫提示:本文旨在说明通用的SQL执行底层原理,对于其优化技术不做考虑,比如谓词下推、投影下推等等。

执行的底层原理

其实上面所说的SQL执行顺序就是所谓的底层原理,当我们在执行SELECT语句时,每个步骤都会产生一张 虚拟表(virtual table) ,在执行下一步骤时,会将该虚拟表作为输入。指的注意的是,这些过程是对用户透明的。

你可以注意到,SELECT 是先从FROM 这一步开始执行的。在这个阶段,如果是多张表进行JOIN,还会经历下面的几个步骤:

获取数据 ( From, Join )

  • 首先会通过 CROSS JOIN 求笛卡尔积,相当于得到虚拟表 vt1-1;
  • 接着通过ON 条件进行筛选,虚拟表 vt1-1 作为输入,输出虚拟表 vt1-2;
  • 添加外部行。我们使用的是左连接、右链接或者全连接,就会涉及到外部行,也就是在虚拟表 vt1-2 的基础上增加外部行,得到虚拟表 vt1-3

过滤数据 ( Where )

经过上面的步骤,我们得到了一张最终的虚拟表vt1,在此表之上作用where过滤,通过筛选条件过滤掉不满足条件的数据,从而得到虚拟表vt2。

分组 ( Group by )

经过where过滤操作之后,得到vt2。接下来进行GROUP BY操作,得到中间的虚拟表vt3。

分组过滤 ( Having )

在虚拟表vt3的基础之上,使用having过滤掉不满足条件的聚合数据,得到vt4。

返回查询字段 ( Select )

当我们完成了条件筛选部分之后,就可以筛选表中提取的字段,也就是进入到 SELECT 和 DISTINCT 阶段。首先在 SELECT 阶段会提取目标字段,然后在 DISTINCT 阶段过滤掉重复的行,分别得到中间的虚拟表 vt5-1 和 vt5-2。

排序与分页 ( Order by & Limit / Offset )

当我们提取了想要的字段数据之后,就可以按照指定的字段进行排序,也就是 ORDER BY 阶段,得到虚拟表 vt6。最后在 vt6 的基础上,取出指定行的记录,也就是 LIMIT 阶段,得到最终的结果,对应的是虚拟表 vt7

详细执行步骤分析

Step 1:获取数据 ( From, Join )

FROM citizen
JOIN city

该过程的第一步是执行From子句中的语句,然后执行Join子句。这些操作的结果是得到两个表的笛卡尔积。

name city_id city_id city_name
tom 3 1 上海
tom 3 2 北京
tom 3 3 杭州
jack 2 1 上海
jack 2 2 北京
jack 2 3 杭州
robin 1 1 上海
robin 1 2 北京
robin 1 3 杭州
jasper 3 1 上海
jasper 3 2 北京
jasper 3 3 杭州
kevin 1 1 上海
kevin 1 2 北京
kevin 1 3 杭州
rachel 2 1 上海
rachel 2 2 北京
rachel 2 3 杭州
trump 3 1 上海
trump 3 2 北京
trump 3 3 杭州
lilei 1 1 上海
lilei 1 2 北京
lilei 1 3 杭州
hanmeiei 1 1 上海
hanmeiei 1 2 北京
hanmeiei 1 3 杭州

在FROM和JOIN执行结束之后,会按照JOIN的ON条件,筛选所需要的行

ON citizen.city_id = city.city_id
name city_id city_id city_name
tom 3 3 杭州
jack 2 2 北京
robin 1 1 上海
jasper 3 3 杭州
kevin 1 1 上海
rachel 2 2 北京
trump 3 3 杭州
lilei 1 1 上海
hanmeiei 1 1 上海

Step 2:过滤数据 ( Where )

获得满足条件的行后,将传递给Where子句。这将使用条件表达式评估每一行。如果行的计算结果不为true,则会将其从集合中删除。

WHERE city.city_name != '上海'
name city_id city_id city_name
tom 3 3 杭州
jack 2 2 北京
jasper 3 3 杭州
rachel 2 2 北京
trump 3 3 杭州

Step 3:分组 ( Group by )

下一步是执行Group by子句,它将具有相同值的行分为一组。此后,将按组对所有Select表达式进行评估,而不是按行进行评估。

GROUP BY city.city_name
GROUP_CONCAT(citizen. name ) city_id city_name
jack,rachel 2 北京
tom,jasper,trump 3 杭州

Step 4:分组过滤 ( Having )

对分组后的数据使用Having子句所包含的谓词进行过滤

HAVING COUNT(*) >= 2

Step 5:返回查询字段 ( Select )

在此步骤中,处理器将评估查询结果将要打印的内容,以及是否有一些函数要对数据运行,例如Distinct,Max,Sqrt,Date,Lower等等。本案例中,SELECT子句只会打印城市名称和其对应分组的count(*)值,并使用标识符“ City”作为city_name列的别名。

SELECT
 city.city_name AS "City",
 COUNT(*) AS "citizen_cnt"
city citizen_cnt
北京 2
杭州 3

Step 6:排序与分页 ( Order by & Limit / Offset )

查询的最后处理步骤涉及结果集的排序与输出大小。在我们的示例中,按照字母顺序升序排列,并输出两条数据结果。

ORDER BY city.city_name ASC
LIMIT 2
city citizen_cnt
北京 2
杭州 3

总结

本文主要剖析了SQL语句的执行顺序和底层原理,基本的SQL查询会分为六大步骤。本文结合具体事例,给出了每一步骤的详细结果,这样会对其执行的底层原理有更加深刻的认识。

到此这篇关于SQL查询的底层运行原理深入分析的文章就介绍到这了,更多相关SQL查询底层运行原理内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 三表左连接查询的sql语句写法

    今天要查询所有realname的username,psw,gname,tel 表结构: 表t1 字段名:t1_id,username,psw 表t2 字段名:t2_id,gname,t1_id //这里一个t1_id对应多个t2_id t3 字段名:t3_id,realname,tel,t1_id //这里一个t1_id对应一个t3_id 遇到的麻烦是当一个realname在t2表中没有内容时,也要将此realname的username,psw,tel等显示出来,直接使用 sql="select

  • SQL重复记录查询的几种方法

    1.查找表中多余的重复记录,重复记录是根据单个字段(peopleId)来判断 复制代码 代码如下: select * from peoplewhere peopleId in (select   peopleId from   people group by   peopleId having count (peopleId) > 1) 2.删除表中多余的重复记录,重复记录是根据单个字段(peopleId)来判断,只留有rowid最小的记录 复制代码 代码如下: delete from peop

  • SQL 查询语句积累

    2006-10-8表数据:tab1id    name  numA     a         9A     b       11B     f         7B     g         8所要结果:A   b    11B    g    8SQL 语句select * from tab1where (num in               (select max(num) from tabl group by id)           )解释说明,之所以要用子查询,目的是要取全部

  • SQL 多表连接查询实现语句

    1.理论 只要两个表的公共字段有匹配值,就将这两个表中的记录组合起来. 个人理解:以一个共同的字段求两个表中符合要求的交集,并将每个表符合要求的记录以共同的字段为牵引合并起来. 语法 select * FROM table1 INNER JOIN table2 ON table1 . field1 compopr table2 . field2 INNER JOIN 操作包含以下部分: 部分 说明 table1, table2 要组合其中的记录的表的名称. field1,field2 要联接的字

  • sql表连接查询使用方法(sql多表连接查询)

    实际的项目,存在多张表的关联关系.不可能在一张表里面就能检索出所有数据.如果没有表连接的话,那么我们就需要非常多的操作.比如需要从A表找出限制性的条件来从B表中检索数据.不但需要分多表来操作,而且效率也不高.比如书中的例子: 复制代码 代码如下: SELECT FIdFROM T_CustomerWHERE FName='MIKE' 这个SQL语句返回2,也就是姓名为MIKE 的客户的FId值为2,这样就可以到T_Order中检索FCustomerId等于2 的记录: 复制代码 代码如下: SE

  • SQL查询的底层运行原理深入分析

    前言 SQL 语言无处不在.SQL 已经不仅仅是技术人员的专属技能了,似乎人人都会写SQL,就如同人人都是产品经理一样.如果你是做后台开发的,那么CRUD就是家常便饭.如果你是做数仓开发的,那么写SQL可能占据了你的大部分工作时间.我们在理解 SELECT 语法的时候,还需要了解 SELECT 执行时的底层原理.只有这样,才能让我们对 SQL 有更深刻的认识.本文分享将逐步分解SQL的执行过程,希望对你有所帮助. 数据准备 本文旨在说明SQL查询的执行过程,不会涉及太复杂的SQL操作,主要涉及两

  • 深入理解以DEBUG方式线程的底层运行原理

    目录 一.Java 运行时数据区域 二.用 DEBUG 的方式看线程运行原理 三.线程运行原理详细图解 四.用 DEBUG 的方式看多线程运行原理 一.Java 运行时数据区域 友情提示:这部分内容可能大部分同学都有一定的了解了,可以跳过直接进入下一小节哈. Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途,以及创建和销毁的时间. 全文我们都将以 JDK 7 的运行时数据区域为例: 先简单解释下线程共享和线程私有是啥意思. 所谓线

  • Spring底层原理深入分析

    目录 bean生命周期 推断构造方法的底层原理 1.使用哪个构造方法 2.如果有参把哪个bean对象赋值给入参 AOP实现原理 spring事务 @Configuration 循环依赖 为什么会出现循环依赖 提前AOP 第一级缓存singletonObjects 第二级缓存earlySingletonObjects 第三级缓存singletonFactories bean生命周期 userService.class--->推断构造方法--->对象--->依赖注入--->初始化前(@

  • PHP底层运行机制与工作原理详解

    最近搭建服务器,突然感觉lamp之间到底是怎么工作的,或者是怎么联系起来?平时只是写程序,重来没有思考过他们之间的工作原理: PHP底层工作原理 图1 php结构 从图上可以看出,php从下到上是一个4层体系 ①Zend引擎 Zend整体用纯c实现,是php的内核部分,它将php代码翻译(词法.语法解析等一系列编译过程)为可执行opcode的处理并实现相应的处理方法.实现了基本的数据结构(如hashtable.oo).内存分配及管理.提供了相应的api方法供外部调用,是一切的核心,所有的外围功能

  • SpringBoot深入分析运行原理与功能实现

    目录 前言 pom.xml文件分析 启动器starter 启动引导类 内置的服务器 内嵌Tomcat定义位置 tomcat运行原理 修改服务器 添加服务器 更换内嵌服务器 前言 我们从以下几个方面研究: SpringBoot的启动依赖 启动器starter有什么作用 启动引导类是怎么运行的 内置的tomcat服务器原理 pom.xml文件分析 我们应用配置第一个就是依赖,这个依赖的作用到底是什么我们仔细来分析一下. 项目中的pom.xml中继承了一个坐标 <parent> <groupI

  • SparkSQl简介及运行原理

    目录 一:什么是SparkSQL? (一)SparkSQL简介 (二)SparkSQL运行原理 (三)SparkSQL特点 二:DataFrame (一)什么是DataFrame? 补充:Spark中的RDD.DataFrame和DataSet讲解 (一)Spark中的模块 (二)RDD和DataFrame的区别 三:SparkSession (一)SparkSession简介 (二)SparkSession实质 (三)SparkSession特点 四:通过RDD创建DataFrame (一)通

  • 分析mybatis运行原理

    目录 一.Mybatis基本认识 1.1.动态代理 1.2.反射 二.Configuration对象作用 三.映射器结构 四.sqlsession执行流程(源码跟踪) 4.1.Executor 4.2.StatementHandler 4.3.结果处理器(ResultSetHandler) 4.4.总结 一.Mybatis基本认识 1.1.动态代理 之前我们知道Mapper仅仅是一个接口,而不是一个逻辑实现类.但是在Java中接口是无法执行逻辑的.这里Mybatis就是通过动态代理实现的.关于动

  • 分析mysql中一条SQL查询语句是如何执行的

    目录 一.MySQL 逻辑架构概览 二.连接器(Connector) 三.查询缓存(Query Cache) 四.解析器(Parser) 五.优化器(Optimizer) 六.执行器 七.小结 一.MySQL 逻辑架构概览 MySQL 最重要.最与众不同的特性就是它的可插拔存储引擎架构(pluggable storage engine architecture),这种架构的设计将查询处理及其他系统任务和数据的存储/提取分离开来.来看官网的解释: The MySQL pluggable stora

  • 分布式架构Redis中有哪些数据结构及底层实现原理

    目录 引言 1.面试官:我看你提到,项目中使用了Reids作为缓存,为什么是Reids而不是其他,Redis有什么优势吗? 2.面试官:刚刚你提到Redis是单线程,为什么单线程模型的Redis性能不减. 3.面试官:那你刚刚说的Redis数据结构都有哪几种,如何选择使用哪种? 深入分析 1.简单动态字符串结构,Redis字符串的实现方式 2.链表数据结构,List底层结构 3.跳跃表,sortedset底层结构 关于缓存的一些算法 常用缓存数据淘汰策略 缓存数据更新策略 总结 引言 面完了负载

  • MySQL中一条SQL查询语句是如何执行的

    目录 前言 1. 处理连接 1.1 客户端和服务端的通信方式 1.1.1 TCP/IP协议 1.1.2 UNIX域套接字 1.1.3 命名管道和共享内存 1.2 权限验证 1.3 查看MySQL连接 2. 解析与优化 2.1 查询缓存 2.2 解析器 & 预处理器(Parser & Preprocessor) 2.2.1 词法解析 2.2.2 语法分析 2.2.3 预处理器 2.3 查询优化器(Optimizer)与查询执行计划 2.3.1 什么是查询优化器? 2.3.2 优化器究竟做了什

随机推荐