MySQL的语句执行顺序
MySQL的语句一共分为11步,如下图所标注的那样,最先执行的总是FROM操作,最后执行的是LIMIT操作。其中每一个操作都会产生一张虚拟的表,这个虚拟的表作为一个处理的输入,只是这些虚拟的表对用户来说是透明的,但是只有最后一个虚拟的表才会被作为结果返回。如果没有在语句中指定某一个子句,那么将会跳过相应的步骤。
**下面我们来具体分析一下查询处理的每一个阶段**
# 1. FROM
**这里也包括from中的子语句**
**FROM是一次查询语句的开端。**
>- 如果是一张表,会直接操作这张表;
>- 如果这个FROM后面是一个子查询,会先执行子查询中的内容,子查询的结果也就是第一个虚拟表T1。**注意**:子查询中的执行流程也是按照本篇文章讲的顺序哦。
>- 如果需要关联表,使用JOIN,请看2,3
# 2. JOIN
如果from后面是多张表,JOIN关联,会首先对前两个表执行一个笛卡尔乘积,这时候就会生成第一个虚拟表T1(注意:这里会选择相对小的表作为基础表);
# 3. ON
对虚表T1进行ON筛选,只有那些符合的行才会被记录在虚表T2中。(注意,这里的这里如果还有第三个表与之关联,会用T2与第三个表进行笛卡尔乘积生产T3表,继续重复3. on步骤生成T4表,不过下面的顺序讲解暂时不针对这里的T3和T4,只是从一个表关联查询T2继续说)
# 4. WHERE
对虚拟表T2进行WHERE条件过滤。只有符合的记录才会被插入到虚拟表T3中。
# 5. GROUP BY
**开始使用select中的别名,后面的语句中都可以使用**
GROUP BY子句将中的唯一的值组合成为一组,得到虚拟表T4。如果应用了GROUP BY,那么后面的所有步骤都只能操作T4的列或者是执行6.聚合函数(count、sum、avg等)。
**注意**:原因在于分组后最终的结果集中只包含每个组中的一行。谨记,不然这里会出现很多问题,下面的代码误区会特别说。
# 6. avg,sum.... 等聚合函数
聚合函数只是对分组的结果进行一些处理,拿到某些想要的聚合值,例如求和,统计数量等,并不生成虚拟表。
# 7. HAVING
应用having筛选器,生成T5。HAVING子句主要和GROUP BY子句配合使用,having筛选器是第一个也是为唯一一个应用到已分组数据的筛选器。
# 8. SELECT
执行select操作,选择指定的列,插入到虚拟表T6中。
若包含over()开窗函数,执行完非开窗函数后select等待执行完开窗函数,然后执行select完,开窗函数通过表数据进行分区和排序,跟select查询中的字段是平行关系,不依赖查询字段。
# 9. DISTINCT
对T6中的记录进行去重。移除相同的行,产生虚拟表T7.
**注意**:事实上如果应用了group by子句那么distinct是多余的,原因同样在于,分组的时候是将列中唯一的值分成一组,同时只为每一组返回一行记录,那么所以的记录都将是不相同的。
# 10. ORDER BY
应用order by子句。按照order_by_condition排序T7,此时返回的一个游标,而不是虚拟表。sql是基于集合的理论的,集合不会预先对他的行排序,它只是成员的逻辑集合,成员的顺序是无关紧要的。对表进行排序的查询可以返回一个对象,这个对象包含特定的物理顺序的逻辑组织。这个对象就叫游标。
**oder by的几点说明:**
因为order by返回值是游标,那么使用order by 子句查询不能应用于表表达式。
order by排序是很需要成本的,除非你必须要排序,否则最好不要指定order by,
order by的两个参数 asc(升序排列) desc(降序排列)
# 11. LIMIT
取出指定行的记录,产生虚拟表T9, 并将结果返回。
limit后面的参数可以是 一个limit m ,也可以是limit m n,表示从第m条到第n条数据。
**注意**:很多开发人员喜欢使用该语句来解决分页问题。对于小数据,使用LIMIT子句没有任何问题,当数据量非常大的时候,使用LIMIT n, m是非常低效的。因为LIMIT的机制是每次都是从头开始扫描,如果需要从第60万行开始,读取3条数据,就需要先扫描定位到60万行,然后再进行读取,而扫描的过程是一个非常低效的过程。所以,对于大数据处理时,是非常有必要在应用层建立一定的缓存机制
---
转载:[如何写优雅的SQL原生语句?](https://juejin.im/post/6844903857722490888#heading-8)