SQL查询的执行顺序





✨ 1、书写顺序和执行顺序


查询是DBMS(OLTP)和OLAP中最常用的操作。我们在理解SELECT语法的时候,还需要了解SELECT执行时的底层原理。只有这样,才能让我们对SQL有更深刻的认识

我们只需要记住和理解SELECT查询时的两个顺序:书写顺序和执行顺序

执行顺序书写顺序
9SELECT
10DISTINCT
6AGG_FUNC
1FROM
3JOIN
2ON
4WHERE
5GROUP BY
7WITH CUBE/ROLLUP
8HAVING
11ORDER BY
12LIMIT

在SELECT语句执行这些步骤的时候,基本每个步骤都会产生一个虚拟表,然后将这个虚拟表传入下一个步骤作为输入。需要注意的是,这些虚拟表隐含在SQL的执行过程中,对于我们来说是不可见的

✨ 2、SQL的执行顺序


所有的查询语句都是从FROM子句开始执行的。一个完整的SELECT查询的执行顺序(以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):

bjidname
1108a
2109d
1106e
2101c
2105b

🌴 成绩表(score):

idnamescore
109d85.0
106e78.0
110f87.0
108a80.0
105b90.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

bjidnamescore
2101cNULL
1108aNULL
2105b90.0
2109d85.0
1106eNULL

🌼 如果在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班级的学生信息,其它班级的学生信息则会被永久过滤

bjidnamescore
2101cNULL
2109d85.0
2105b90.0

总结来说就是,ON会显示所有匹配条件的值,不匹配条件的数据补NULL;WHERE只显示满足条件的数据



参考文章:https://www.cnblogs.com/antLaddie/p/17175396.html


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值