对mysql执行顺序的设计,我充满了敬佩。(说不定SQL之父也是一个斗地主的好手,哈哈调侃)
记得以前学SQL语言的时候,总是从说规则要求如下的执行顺序:
1. FROM 标识了这条语句要查询的数据源。
读取命令:inner join 计算笛卡尔乘积/on过滤/外加列
2. WHERE 对临时表进行过滤,满足where子句的列,生成新表。
能优先在这里筛选,就尽量在这里筛选,考虑到性能。
3. GROUP BY 拿到上表,看groupby后面的列是什么,然后查看该列,将所有值相同的行划分到一组中,生成新表。
4. HAVING 对上表的每个分组,使用聚合函数(聚合函数的定义就是要求对分组数据进行处理)处理,根据结果判断,找出满足条件的组。(如果select中有查询的聚合函数,也认为在这里执行,只是进行了计算,但没有判断和筛选)
5. SELECT 筛选出要打印的 列,生成新表。
distinct 根据列(属性)查行值相同(属性值)去重
distinct 列
order by 排序
order by 列名称 或者 列别名 asc 或者 desc
limit 限制返回行数
但是纯记忆,真的是一件很痛苦的事情、这里我想说一下我的理解:
每个步骤都会产生一个虚拟表,该虚拟表被用作下一个步骤的输入。这些虚拟表对调用者(客户端应用程序或者外部查询)不可用。只是最后一步生成的表才会返回 给调用者。
![]() |
通过上面这句话:建立一起一个清晰的逻辑,写SQL是为了什么?是为了查询数据。那么就如同斗地主发牌一样,大家绝对是希望从拿牌开始理清逻辑,然后一直到打完牌,牌都是有序且越来越少的。
有这种感觉以后,再来设想一下SQL命令的执行顺序,如果是你来设计,你会怎么设计?绝对是希望最开始就尽可能的找到符合要求的所有情况,然后就想切豆腐一样,横一刀竖一刀的处理,把这个方形大表切成我们想要保留到最后的那一部分。
另外有经验的人都知道,在一张表中,行的数量是远高于列的数量的,所以在数据的筛选中,我们一定要优先、尽快、大刀阔斧的力争在每一次的操作中都剔除掉尽可能多的不符合要求的行,那样底层执行速度在每一个阶段才会飞速提升,而不是先处理列,所以这也就解释了为什么select操作的是列,却靠后执行的原因。
好了有了这么一个判断,我们再来看一看关键字的执行顺序,我觉得设计sql的人真是一个天才,我充满了敬佩。(说不定也是一个斗地主的好手)
以两张表关联为例:
Step 1 from + join
拿到两张表,这没什么好说的,表都没到,还处理什么数据??两张表拿到后的第一步,按我们分析的逻辑,应该先尽可能的生成一张巨大的表,不能说后面中途处理一半发现哎,好像缺点数据,再回头找点数据像补丁一样补回来吧。所以要先拿两个表进行笛卡尔乘积,算出一个虚拟的巨表。
Step 2 on
拿到了笛卡尔表后,准备操作这个表了,on关键字表达的是关注、关心、关于,你也可以理解为关联,但是我更喜欢理解为关心,也就是说拿到了这么大一张表后,我更关心的条件是什么,笛卡尔乘积会多生成很多的行,经过on的筛选,现在会剔除很多行,递交到下一步的表就离我们想要的查询结果更近了一步。
Step 3 外加列(有时候我们会在seletc中定义一些自己要的列)
这个外加列放在这里执行考虑的逻辑就是,使表在最开始就尽可能的大,同时不应该放在on之前,从使用便利角度来说,没人会特意去查询一个笛卡尔乘积表,所以join之后一定是有on的,on会剔除大量的无效行。如果外加列在on之前就生成,就会导致刚生成巨量数据,又在这一列中砍去了很多行。所以应该放在on后面,那可能会有人问了,照你这么说,外加列应该放到最后面才对,那样生成的数据最少,效率更高。不对!逻辑不对!因为数据应该在一开始就定义成功,后续的操作都是基于这张巨大的表横一刀竖一刀裁剪,就如同你斗地主你希望最开始自己就把自己的牌理清,还是扣一半,把手上的牌打一会再把扣着的牌加进来继续理清继续打?
Step 4 where
上一步递交下来的表,我们仍然要继续裁剪行,where根据柯林斯词典,有“”在这种情况下“”的意思,所以可以把尽可能的的筛选条件放到这里,尽可能多的在这一步剔除足够多的行,那样数据表高度就会被进一步压缩。
Step 5 group by 分组
Group by 一定不是在 where 之前和 on 之前执行,因为分组是把数据变得有序,如果花费了大量的时间,把所有的数据分组放到一起后,却发现有许多行需要剔除,这真实太影响性能了,毕竟分组还是很耽误时间的。
Step 6 having ‘有’
Having 往往会结合聚合函数对分组后的表进行操作,这样就有可能进一步的剔除行,压缩递交过来的表的高度。
step 7 select 挑选
表的高度已经被压缩不少了,下面可以处理列了,挑选出自己要打印的列。这里相当于开始削减表的宽度了。这之后,列就稳定了。
Step 8 distinct 去重
去重是根据列去重,所以需要等待列稳定了。
去重不宜发生在行还在动态变化的时候(表的高度的压缩还没有完成的时候),因为这样会发生很多次的跨行比较值是否相同,会降低效率,所以放在select后面执行合情合理。这里又削减了表的高度。
Step 9 order by
根据列排序,所以需要等待列稳定了
排序也是能提升性能的地方,排序一定是要求结果集稳定之后,才排序,不然我辛苦排序的数据,被你删除行的时候删除了怎么办?
Step 10 limit
大功告成,开始裁剪结果集,限制呈现的数量。