查询处理
对于查询处理,可将其分为逻辑查询处理和物理查询处理,逻辑查询处理表示执行查询应该产生什么样的结果,而物理查询代表MySQL数据库是如何得到该结果的。两种查询的方法可能完全不同,但是得到的结果必定是相同的。
逻辑查询处理
逻辑查询的顺序以及步骤序号
(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>
- 执行笛卡尔积
第一步需要做的是对from子句前后的两张表进行笛卡尔积操作,也称做交叉连接(cross join),生成虚拟表vt1,如果from子句前的表中包含a行数据,from子句后的表中包含b行数据,那么虚拟表vt1将包含a*b行数据。
- 应用on过滤器
select 查询一共有3个过滤过程,分别是on、where、having。on是最先执行的过滤过程。这里产生虚拟表vt2
在关系型数据库中起逻辑表达式作用的并非只有两种(true and false),还有一种称为三值逻辑的表达式null。对于在on过滤条件下的null值比较,此时比较结果为unknown,被视为false处理。但在下面两种情况下认为两个null值的比较是相等的:
- group by 子句把所有null值分到同一组。
- order by 子句中把所有null值排列在一起。
- 添加外部行
这一步只有在连接类型为outer join时发生,如left/right/full outer join。虽然大部分时间可以省略outer,但是outer表示外部行。添加外部行的工作就是在vt2表的基础上添加保留表中被过滤条件过滤掉的数据,非保留表中的数据被赋予null值,最后生成虚拟vt3。
- 应用where过滤器
在当前应用where过滤器时,有两种过滤是不被允许的:
- 由于数据还没有被分组,因此现在还不能在where过滤器中使用where_condition=min(col)这类对统计的过滤。
- 由于没有进行列的选取操作,因此在select中使用列的别名也是不被允许的。
产生虚拟表vt4
- 分组
group by col_name
对指定的列进行分组,产生虚拟表vt5
- 应用rollup或cube
如果指定了rollup选项,那么将创建一个额外的记录添加到虚拟表vt5的最后,并生成虚拟表vt6。
对于cube选项,MySQL数据库虽然支持该关键字的解析,但是并未实现该功能。
- having过滤器
having是对分组条件进行过滤的筛选器。
having count(col_name) < 2
需要注意的是,在这个分组中不能使用count(1),count(*),因为这会把通过outer join添加的行统计入内而导致最终查询结果与预期结果不同。
- 处理select列表
虽然select是查询中最先被指定的部分,但是到现在才处理。有一点需要注意的是,列的别名不能在select中的其他别名表达式中使用。
- 应用distinct子句
如果在查询中指定了distinct子句,则会创建一张内存临时表,这张内存临时表的表结构和上一步产生的虚拟表一样,不同的是对进行distinct操作的列增加了一个唯一索引,以此来去除重复数据。
- order by子句
根据order by子句中指定的列对上一步输出的虚拟表进行排列,返回新的虚拟表。
- limit子句
在该步骤中应用limit子句,从上一步骤的虚拟表中选出从指定位置开始的指定行数据,对于没有应用order by的limit子句,结果同样可能是无序的,因此limit子句通常和order by子句一起使用。
MySQL数据库的limit支持如下形式的选择:
limit n,m
表示从第n条记录开始选择m条记录。
物理查询处理
MySQL数据库层有parser和optimizer两个组件,parser的工作就是分析SQL语句,而optimizer的工作就是对这个SQL语句进行优化,选择一条最优的路径来选取数据。