关于sql和MySQL的语句执行顺序(必看!!!)
sql语句执行顺序
MySql执行顺序及执行计划
建议看原文
1.from
2.join
3.on
4.where
5.group by
6.avg,sum…(SQL函数)
7.having
8.select
9.distinct
10.order by
从这个顺序中我们不难发现,所有的查询语句都是从from开始执行的,在执行过程中,每个步骤都会为下一个步骤生成一个虚拟表,这个虚拟表将作为下一个执行步骤的输入。
(1) SELECT (2)DISTINCT<select_list>
(3) FROM <left_table>
(4) <join_type> JOIN <right_table>
(5) ON <join_condition>
(6) WHERE <where_condition>
(7) GROUP BY <group_by_list>
(8) WITH {CUBE|ROLLUP}
(9) HAVING <having_condition>
(10) ORDER BY <order_by_condition>
(11) LIMIT <limit_number>
第一步:对FROM子句中的左表
第二步:接下来便是应用ON筛选器,ON中的逻辑表达式将应用到 vt1 中的各个行,筛选出满足on逻辑表达式(
第三步:如果是OUTER JOIN 那么这一步就将添加外部行,(left outer jion 就把左表在第二步中过滤的添加进来,如果是right outer join 那么就将右表在第二步中过滤掉的行添加进来)这样生成虚拟表 VT3 。如果FROM子句中的表数目多于两个表,那么就将VT3和第三个表连接从而计算笛卡尔乘积,生成虚拟表,该过程就是一个重复1-3的步骤,最终得到一个新的虚拟表VT3。
第四步:应用WHERE筛选器,对上一步生产的虚拟表引用WHERE筛选器,生成虚拟表VT4,
在这有个比较重要的细节不得不说一下,对于包含outer join子句的查询,就有一个让人感到困惑的问题,到底在on筛选器,还是用where筛选器指定逻辑表达式呢?
on和where的最大区别在于,如果在on应用逻辑表达式那么在第三步outer join中还可以把移除的行再次添加回来,而where的移除的最终的。
举个简单的例子:
有一个学生表(班级,姓名)和一个成绩表(姓名,成绩),我现在需要返回一个x班级的全体同学的成绩,但是这个班级有几个学生缺考,也就是说在成绩表中没有记录。
为了得到我们预期的结果我们就需要在on子句指定学生和成绩表的关系(学生.姓名=成绩.姓名)那么我们是否发现在执行第二步的时候,对于没有参加考试的学生记录就不会出现在vt2中,因为他们被on的逻辑表达式过滤掉了,但是我们用left outer join就可以把左表(学生)中没有参加考试的学生找回来,因为我们想返回的是x班级的所有学生,如果在on中应用学生.班级='x’的话,left outer join会把x班级的所有学生记录找回,所以只能在where筛选器中应用学生.班级=‘x’ 因为它的过滤是最终的。
第五步:根据GROUP BY子句中的列(
第六步:应用cube或者rollup选项,为VT5生成超组,生成VT6.
第七步:应用HAVING筛选器,生成VT7。
HAVING筛选器是第一个也是为唯一一个应用到已分组数据的筛选器。
第八步:处理SELECT子句。将VT7中的在SELECT中出现的列筛选出来。生成VT8.
第九步:应用DISTINCT子句,VT8中移除相同的行,生成VT9。
事实上如果应用了GROUP BY子句那么DISTINCT是多余的。
原因同样在于,分组的时候是将列中唯一的值分成一组,同时只为每一组返回一行记录,那么所以的记录都将是不相同的。
第十步:应用ORDER BY子句。按照order_by_condition排序VT9,此时返回的一个游标,而不是虚拟表。sql是基于集合的理论的,集合不会预先对他的行排序,它只是成员的逻辑集合,成员的顺序是无关紧要的。
对表进行排序的查询可以返回一个对象,这个对象包含特定的物理顺序的逻辑组织。这个对象就叫游标。 正因为返回值是游标,那么使用order by 子句查询不能应用于表达式。排序是很需要成本的,除非你必须要排序,否则最好不要指定order by.
最后,在这一步中是第一个也是唯一一个可以使用select列表中别名的步骤。
第十一步:LIMIT: 取出指定行的记录,产生虚拟表VT10,并返回给查询用户
开始->FROM子句->WHERE子句->GROUP BY子句->HAVING子句->SELECT子句->ORDER BY子句->LIMIT子句->最终结果
select 考生姓名, max(总成绩) as max总成绩
from tb_Grade
where 考生姓名 is not null
group by 考生姓名
having max(max总成绩) > 600
order by max总成绩
在上面的示例中 SQL 语句的执行顺序如下:
执行计划就是sql的执行查询的顺序,以及如何使用索引查询,返回的结果集的行数
①.id sql执行计划的顺序 或子查询表的执行顺序
id一样,按照顺序执行;id越大,执行的优先级就越高(如子查询)
②.select_type 表示查询中每个select子句的类型
a.SIMPLE:查询中不包含子查询或者UNION
b.查询中若包含任何复杂的子部分,最外层查询则被标记为:PRIMARY
c.在SELECT或WHERE列表中包含了子查询,该子查询被标记为:SUBQUERY
d.在FROM列表中包含的子查询被标记为:DERIVED(衍生)
e.若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在 FROM子句的子查询中,外层SELECT将被标记为:DERIVED
f.从UNION表获取结果的SELECT被标记为:UNION RESULT
③.type
MySQL在表中找到所需行的方式,又称“访问类型”,常见类型如下:
由左至右,由最差到最好
ALL:全表扫描
index:index类型只遍历索引树
索引的存在形式是文件,按存储结构划分):FULLTEXT,HASH,BTREE,RTREE。
range:索引范围扫描
对索引字段进行范围查询,使用in则是使用rang范围查询; 使用">" ,"<" 或者 “between” 都是可以使用索引的,但是要控制查询的时间范围,一般查询数据不要超过数据总数的 15%
ref:非唯一性索引
类似 select count(1) from age = ‘20’;
eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
④.key
表示在执行语句用到的索引
查询中若使用了覆盖索引,则该索引仅出现在key列表中
覆盖索引:查询数据只需要通过索引就可以查询出,如55万条数据,使用索引,立刻可以查询出 2000条数据,同时Extra字段是Using index
⑤.Extra
Using index : 使用覆盖索引的时候就会出现
using index condition:查找使用了索引,但是需要回表查询数据
Using where :在查找使用索引的情况下,需要回表去查询所需的数据
using index & using where:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据
Using temporary:需要使用临时表来存储结果集,常见于排序和分组查询
Using filesort:无法利用索引完成的排序操作称为“文件排序”;
很多场景都是索引是一个字段,order by 排序的字段与索引字段不一致,导致的Using fileSort;
此时可以给排序字段和where条件字段,添加为组合索引,同时保证索引查询的数据不超过总量的15%,避免fileSort
注:回表的含义是,先根据索引查询数据,然后在根据确定的数据id和查询条件再去查询具体的数据的过程