SQL查询的执行流程
- ✨ 1、书写顺序和执行顺序
- ✨ 2、SQL的执行流程
- ✨ 3、WHERE与ON的使用
✨ 1、书写顺序和执行顺序
查询是DBMS(OLTP)和OLAP中最常用的操作。我们在理解SELECT语法的时候,还需要了解SELECT执行时的底层原理。只有这样,才能让我们对SQL有更深刻的认识
我们只需要记住和理解SELECT查询时的两个顺序:书写顺序和执行顺序
执行顺序 | 书写顺序 |
---|---|
9 | SELECT |
10 | DISTINCT |
1 | FROM |
3 | JOIN |
2 | ON |
4 | WHERE |
5 | GROUP BY |
7 | WITH CUBE/ROLLUP |
6 | AGG_FUNC |
8 | HAVING |
11 | ORDER BY |
12 | LIMIT |
在SELECT语句执行这些步骤的时候,基本每个步骤都会产生一个虚拟表,然后将这个虚拟表传入下一个步骤作为输入。需要注意的是,这些虚拟表隐含在SQL的执行过程中,对于我们来说是不可见的
✨ 2、SQL的执行流程
所有的查询语句都是从FROM子句开始执行的。一个完整的SELECT查询SQL执行流程(以MySQL为例)如下:
- 🎈第一步(FROM):对FROM子句中的前两表执行笛卡尔积(交叉连接),生成虚拟表vt1
- 🎈第二步(ON):对vt1应用ON筛选器,筛选出满足ON表达式的行,生成虚拟表vt2
- 🎈第三步(JOIN):保留表中未匹配的行将作为外部行添加到vt2。如果是LEFT JOIN,将左表在第二步中过滤的行添加进来;如果是RIGHT JOIN,将右表在第二步中过滤的行添加进来,生成虚拟表vt3
💡 注意:如果FROM子句中有多于两个表,那么就将vt3和第三个表连接计算笛卡尔乘积,生成虚拟表,即重复1-3步骤,最终得到一个新的虚拟表vt3
- 🎈第四步(WHERE):对vt3应用WHERE筛选器,生成虚拟表vt4
- 🎈第五步(GROUP BY):GROUP BY子句将对vt4按指定列唯一值进行分组,每一组仅包含一行,得到虚拟表vt5
💡 注意:由于vt5的结果集中每个组只包含一行,因此后面的操作只能得到vt5的列或基于每组内的一行数据操作
- 🎈第六步(聚合函数):对每组内的一行数据应用聚合函数,生成虚拟表vt6
- 🎈第七步(WITH CUBE/ROLLUP):如果与GROUP BY一起使用,计算不同维度层次结构级别下的聚合(vt6)
- 🎈第八步(HAVING):对vt5的每组数据或vt6聚合结果应用HAVING筛选器,生成虚拟表vt7
💡 注意:这一步是第一个可以使用SELECT列的别名的步骤
- 🎈第九步(SELECT):从vt7中筛选列,生成虚拟表vt8
- 🎈第十步(DISTINCT):移除vt8中相同的行(去重),生成虚拟表vt9
💡 注意:如果执行了GROUP BY,DISTINCT将是多余的。因为GROUP BY将列中相同值的分成一组,同时只为每一组返回一行记录,那么所有的记录都将是不相同的
- 🎈第十一步(ORDER BY):对vt9按指定字段排序,生成一个游标vc10,而不是虚拟表
💡 注意:SQL是基于集合理论的,集合不会预先对它进行排序,它只是成员的逻辑集合,成员的顺序是无关紧要的。对表进行排序的查询返回一个游标对象,这个对象是包含特定物理顺序的逻辑组织。正因为返回值是游标,所以使用ORDER BY子句查询不能应用于表达式。排序是很需要成本的,除非必须要排序,否则最好不要指定ORDER BY。另外,这一步是第二个可以使用SELECT列的别名的步骤
- 🎈第十二步(LIMIT):从游标vc10开始处选择指定数量的行,生成最终表返回给客户端
✨ 3、WHERE与ON的使用
对于包含OUTER JOIN子句的查询,到底是在ON筛选器里指定逻辑过滤条件还是用WHERE筛选器指定逻辑过滤条件呢?
实际上,ON和WHERE的最大区别在于,如果在ON中应用逻辑表达式,那么在第三步OUTER JOIN中还可以把过滤的行再次添加回来匹配。而WHERE一旦被过滤就找不回来了
简单来说就是,当有外关联表时(左连接和右连接),ON主要是针对外关联表进行筛选,主表信息保留;当不是外关联表时,两者作用相同
例如左外连接时,首先执行ON,筛选掉外关连表中不符合ON表达式的数据,JOIN时再将这部分数据进行与主表匹配;而WHERE的筛选是对ON关联后的结果进行整体筛选
假设有一个学生表(班级,ID,姓名)和一个成绩表(ID,姓名,成绩):
create table stu (bj string,id string,name string
)insert into stu values
('1', '108', 'a'),
('2', '105', 'b'),
('2', '101', 'c'),
('2', '109', 'd'),
('1', '106', 'e');create table score (id string,name string,score double
)insert into score values
('108', 'a', 80),
('105', 'b', 90),
('110', 'f', 87),
('109', 'd', 85),
('106', 'e', 78);
🌴 学生表(stu):
bj | id | name |
---|---|---|
1 | 108 | a |
2 | 109 | d |
1 | 106 | e |
2 | 101 | c |
2 | 105 | b |
🌴 成绩表(score):
id | name | score |
---|---|---|
109 | d | 85.0 |
106 | e | 78.0 |
110 | f | 87.0 |
108 | a | 80.0 |
105 | b | 90.0 |
现在需要返回一个X班级的全体同学的成绩,但是这个班级有几个学生缺考,也就是说在成绩表中没有记录。为了得到预期的结果,首先就需要在ON子句指定学生表和成绩表的关系(学生.ID=成绩.ID)
🌼 如果在ON子句中应用学生.班级=X:
select a.bj,a.id,a.name,b.score
from stu a
left join score b
on a.id=b.id and a.bj='2'
LEFT JION会把班级X在第二步中过滤的记录找回主表(保留表)匹配,匹配不到的数据补NULL
bj | id | name | score |
---|---|---|---|
2 | 101 | c | NULL |
1 | 108 | a | NULL |
2 | 105 | b | 90.0 |
2 | 109 | d | 85.0 |
1 | 106 | e | NULL |
🌼 如果在WHERE中筛选学生.班级=X:
select a.bj,a.id,a.name,b.score
from stu a
left join score b
on a.id=b.id
where a.bj='2'
则只打印X班级的学生信息,其它班级的学生信息则会被永久过滤
bj | id | name | score |
---|---|---|---|
2 | 101 | c | NULL |
2 | 109 | d | 85.0 |
2 | 105 | b | 90.0 |
总结来说就是,ON会显示所有匹配条件的值,不匹配条件的数据补NULL;WHERE只显示满足条件的数据
参考文章:https://www.cnblogs.com/antLaddie/p/17175396.html