sql语句执行顺序
0.整体说明
平时我们写sql的时候有没有注意sql语句是按照什么顺序执行的呢?
本篇文章就来介绍下这个顺序,参考了《MySQL技术内幕 SQL编程》,有兴趣可以去观摩一下
(8)SELECT (9)DISTINCT <select_list>
(1)FROM <left_table>
(3)<join_type> JOIN <right_table>
(2)ON <join_condition>
(4)WHERE <where_condition>
(5)GROUP BY <group_by_list>
(6)WITH {CUBE|ROLLUP}
(7)HAVING <having_condition>
(10)ORDER BY <order_by_list>
(11)LIMIT <limit_number>
如上诉代码所示,sql的执行顺序是按照这个顺序执行的 总共11个步骤,第一步是from,最后一步是limit,每个操作都会产生一张虚拟表,这些虚拟表对用户不是透明的,只有最后一步生成的虚拟表才会返回给用户。 具体每一步的内容如下:
- FROM:对FROM子句中的左表和右表执行笛卡尔积,产生虚拟表VT1
- ON:对虚拟表VT1应用ON筛选,只有那些符合的行才被插入虚拟表VT2中
- JOIN:如果指定了OUTER JOIN(如LEFT OUTER JOIN ,RIGTH OUTER JOIN),那么保留表中未匹配的行作为外部行添加到虚拟表VT2中,产生虚拟表VT3。如果FROM子句含两个以上表,则对上一个连接生成的结果表VT3和下一个表重复执行步骤1~步骤3,直到处理完所有的表为止
- WHERE: 对虚拟表VT3应用VT3应用WEHRE过滤条件,只有符合的记录才被插入虚拟表VT4中
- GROUP BY:根据GROUP BY 子句中的列,对VT4中的记录进行分组操作,产生VT5
- CUBE|ROLLUP:对表VT5进行CUBE或ROLLUP操作,产生表VT6
- HAVING:对虚拟表VT6应用HAVING过滤器,只有符合的记录才被插入虚拟表VT7中
- SELECT:选定指定的列,插入到虚拟表VT8中
- DISTINCT:去除重复数据,产生虚拟表VT9
- ORDER BY:将虚拟表VT9中的记录按照进行排序操作,产生虚拟表VT10
- LIMIT:取出指定行的数据,产生虚拟表VT11,并返回给查询用户
数据准备如下:
CREATE TABLE `student` (
`student_id` int(11) DEFAULT NULL,
`student_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
`subject` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL
);
INSERT INTO `student` VALUES (1, '张三', '语文');
INSERT INTO `student` VALUES (2, '李四', '数学');
INSERT INTO `student` VALUES (3, '王二', '语文');
INSERT INTO `student` VALUES (4, '权一', '音乐');
INSERT INTO `student` VALUES (5, '孙五', '语文');
CREATE TABLE `orders` (
`student_id` int(11) DEFAULT NULL,
`order_id` int(11) DEFAULT NULL
);
INSERT INTO `orders` VALUES (1, 1);
INSERT INTO `orders` VALUES (2, 2);
INSERT INTO `orders` VALUES (3, 1);
INSERT INTO `orders` VALUES (4, 4);
1.笛卡尔积
对From子句前后的两张表进行笛卡尔积操作,生产虚拟表VT1。
如果FROM子句前的表中包含a行数据,From子句后的表中包含b行数据,那么虚拟表VT1中将包含a*b行数据。
select *
from student t1
join orders t2
![d9bb23e7607fe87720225bc939c8c286.png](https://i-blog.csdnimg.cn/blog_migrate/7443de8537cd6a679cb48de68fa8a04b.png)
2.ON关联过滤
SELECT查询一共有3个过滤过程,分别是ON,WHERE,HAVING。
ON是最先执行的过滤过程。根据上一个步骤产生的虚拟表VT1,应用ON进行过滤
select *
from student t1
join orders t2
on t1.student_id = t2.student_id
![fe288f94c19c4fd4192c3724b34fdd2c.png](https://i-blog.csdnimg.cn/blog_migrate/91969147f4d7bea73271cf64daa39ee4.png)
3.添加外部行
这一步只有在连接类型为OUTER JOIN时才发生,如LEFT OUTER JOIN,RIGHT OUTER JOIN,FULL OUTER JOIN。
虽然在大多数时候我们可以省略OUTER关键词,但OUTER代表的就是外部行。
LEFT OUTER JOIN把左表记为保留表,RIGHT OUTER JOIN把右表记为保留表,FULL OUTER JOIN把左右表都记为保留表。
添加外部行的工作就是在VT2表的基础上添加保留表中被过滤条件过滤掉的数据,非保留表的数据被赋予NULL值,最后生成虚拟表VT3
由于孙五没有排序信息,所以排序表相关信息赋值为NULL
select *
from student t1
left join orders t2
on t1.student_id = t2.student_id
![a24ed5bff27ed01495dba838307babd4.png](https://i-blog.csdnimg.cn/blog_migrate/25b2938319c90be0cf4e66135c091c96.png)
如果需要连接表的数量大于2,则对虚拟表VT3重做步骤1-步骤3,最后产生的虚拟表作为下一个步骤的输出
4.WHERE条件过滤
对上一个步骤产生的虚拟表VT3进行WHERE条件过滤,只有符合 where 条件 的记录才会输出到虚拟表VT4中。
在当前应用WHERE过滤器时,有两种过滤是不被允许的
- 由于数据还没有分组,因此现在还不能在WHERE过滤中使用where_condition=MIN(col)这类对统计的过滤
- 由于没有进行列的选取操作,因此在SELECT中使用列的别名也是不被允许的,如SELECT student_name as c FROM student WHERE c = "张三" 是不允许出现的
select *
from student t1
left join orders t2
on t1.student_id = t2.student_id
where t1.subject = '语文'
![461130f9e9ecfc637098399be5d77480.png](https://i-blog.csdnimg.cn/blog_migrate/72fb837d26937d2e559d3a30a570dd2e.jpeg)
5.分组
在本步骤中根据指定的列对上个步骤中产生的虚拟表进行分组,最后得到虚拟表VT5
select *
from student t1
left join orders t2
on t1.student_id = t2.student_id
where t1.subject = '语文'
group by order_id
![8055ae4ca5425d605432078a1bf986be.png](https://i-blog.csdnimg.cn/blog_migrate/a5a0d253e025305e5f9fc87b990f4b45.jpeg)
6.ROLLUP或者CUBE
如果指定了ROLLUP选项,那么将创建一个额外的记录添加到虚拟表VT5的最后,并生成虚拟表VT6。
因为我们的查询并未用到ROLLUP,所以将跳过本步骤。
对于CUBE选项,MySQL数据库虽然支持该关键字的解析,但是并未实现该功能
7.HAVING 过滤
这是最后一个条件过滤了,之前已经分别应用了ON和WHERE过滤。
在该步骤中对于上一步产生的虚拟表应用HAVING过滤,HAVING是对分组条件进行过滤的筛序。
select *
from student t1
left join orders t2
on t1.student_id = t2.student_id
where t1.subject = '语文'
group by order_id
having count(order_id)<2
由于order_id 张三有两个所以被过滤掉了
![af97892c1699735f09b9244b7028b9ec.png](https://i-blog.csdnimg.cn/blog_migrate/985f79c2988a0b809c14b8df6347b304.jpeg)
8.SELECT列表
虽然SELECT是查询中最先被指定的部分,但是直到这时才真正进行处理。
在这一步中,将SELECT中指定的列从上一步产生的虚拟表中选出
select
t1.student_name,
count(*)
from student t1
left join orders t2
on t1.student_id = t2.student_id
where t1.subject = '语文'
group by t1.student_name
having count(order_id)<2
![865a6df8274db961a0fa4728d60c5af8.png](https://i-blog.csdnimg.cn/blog_migrate/96ba4f6f95a82a1e0625f3918374e9ef.png)
9.DISTINCT语句
如果在查询中指定了DISTINCT子句,则会创建一张内存临时表(如果内存中存放不下就放到磁盘上)。这张内存临时表的表结构和上一步产生的虚拟表一样,不同的是对进行DISTINCT操作的列增加了一个唯一索引,以此来去除重复数据。
由于在这个SQL查询中未指定DISTINCT,因此跳过本步骤。另外对使用了GROUP BY的查询,再使用DISTINCT是多余的,因为已经进行分组,不会移除任何行
10.ORDER BY语句
根据ORDER BY子句中指定的列对上一个输出的虚拟表进行排列,返回新的虚拟表
select
t1.student_name,
count(*)
from student t1
left join orders t2
on t1.student_id = t2.student_id
where t1.subject = '语文'
group by t1.student_name
having count(order_id)<2
order by t1.student_name desc
![a1be0425d5777654a4b321a6e59ade01.png](https://i-blog.csdnimg.cn/blog_migrate/d94b465fbcf08701a35a763163ff67f3.png)
11.LIMIT语句
在该步骤中应用LIMIT子句,从上一步骤的虚拟表选出从指定位置开始的指定行数据。
对于没有应用ORDER BY的LIMIT子句,结果同样可能是无序的,因此LIMIT子句通常和ORDER BY子句一起使用
select
t1.student_name,
count(*)
from student t1
left join orders t2
on t1.student_id = t2.student_id
where t1.subject = '语文'
group by t1.student_name
having count(order_id)<2
order by t1.student_name desc
limit 1
![db19a2a1f476d73d53a378b210a8592a.png](https://i-blog.csdnimg.cn/blog_migrate/787209df74b011982db037c688ed83d6.png)