一条sql的执行(mysql)
select 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_specification>
执行顺序:
笛卡尔积---on---join类型---where---group by---with{cube | rollup}---having---select---distinct---order by---limit
ps:
limit works on MySQL and PostgreSQL, top works on SQL Server, rownum works on Oracle.
细解
-
on vs where 两者效果可能一样(内连接),但on是连接两表做笛卡尔积时的连接条件,where连接之后的筛选条件。
-
where vs having
-
where 在对查询结果进行分组前(
group by 之前
),将不符合where条件的行去掉,即在分组之前过滤数据, where条件中不能包含聚集函数,使用where条件过滤出特定的行。 -
having 筛选满足条件的组(
group by 之后
,having是专门搭配group by干活的),即在分组之后过滤数据,条件中经常包含聚集函数,使用having条件过滤出特定的组,也可以使用多个分组标准进行分组。
-
-
join、left join、right join
eg table_a,table_b
- left join 以左为准,右可以为空, 记录数>=table_a总数
- right join 以右为准,左可以为空,记录数>=table_b总数
-
group by
-
先对某一列或多列进行分组,然后在分组内进行相应的操作,一般和count,max等聚集函数一起使用。
-
聚集函数与列不能同时出现在select子句中,除非这个列是group by子句的分组列,参见下错误例子
-
当查询语句中有group by子句时,聚集函数作用的对象是一个分组,而不是整个查询的结果,没有group by的话是整个查询结果
select DepartmentId,Name,count(Salary) from Employee group by Name;
Error Code: 1055. Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column ‘test.Employee.DepartmentId’ which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
-
-
order by
对某一列进行排序,发生在select,distinct之后
-
limit
取出结果的几个 eg:limit 1,2从1开始的2个。 查询结果是从0 开始的,db会扫出这n+m条记录,然后筛选。所以分页慎用limit m,n
-
union和union all
跨库查询,union会去重,union all 不会去重
-
group by 之后,可以通过加order by null避免文件排序
where条件在数据库中提取
SQL语句中的where条件,使用以上的提取规则,最终都会被提取到Index Key
(First Key
& Last Key
),Index Filter
与Table Filter
之中。
- Index Key
- Index First Key,只是用来定位索引的起始范围,因此只在索引第一次Search Path(沿着索引B+树的根节点一直遍历,到索引正确的叶节点位置)时使用,一次判断即可,
- 从索引的第一个键值开始,检查其在where条件中是否存在,若存在并且条件是=,则将对应的条件加入Index First Key之中,继续读取索引的下一个键值,使用同样的提取规则
- 若存在并且条件是>=或者>,则将对应的条件加入Index First Key中,同时终止Index First Key的提取;若不存在,同样终止Index First Key的提取。
-
Index Last Key,用来定位索引的终止范围,因此对于起始范围之后读到的每一条索引记录,均需要判断是否已经超过了Index Last Key的范围,若超过,则当前查询结束;
- 从索引的第一个键值开始,检查其在where条件中是否存在,若存在并且条件是=,则将对应条件加入到Index Last Key中,继续提取索引的下一个键值,使用同样的提取规则
- 若存在并且条件是 <或<= ,则将条件加入到Index Last Key中,同时终止提取;若不存在,同样终止Index Last Key的提取。
- Index First Key,只是用来定位索引的起始范围,因此只在索引第一次Search Path(沿着索引B+树的根节点一直遍历,到索引正确的叶节点位置)时使用,一次判断即可,
-
Index Filter 用于过滤索引查询范围中不满足查询条件的记录,因此对于索引范围中的每一条记录,均需要与Index Filter进行对比,若不满足Index Filter则直接丢弃,继续读取索引下一条记录;
1. 从索引列的第一列开始,检查其在where条件中是否存在:若存在并且where条件仅为 =,则跳过第一列继续检查索引下一列,下一索引列采取与索引第一列同样的提取规则 2. 若where条件为 >=、>、<、<= 其中的几种,则跳过索引第一列,将其余where条件中索引相关列全部加入到Index Filter之中 3. 若索引第一列的where条件包含 =、>=、>、<、<= 之外的条件,则将此条件以及其余where条件中索引相关列全部加入到Index Filter之中 4. 若第一列不包含查询条件,则将所有索引相关条件均加入到Index Filter之中。
- Table Filter 最后一道where条件的防线,用于过滤通过前面索引的层层考验的记录,此时的记录已经满足了Index First Key与Index Last Key构成的范围,并且满足Index Filter的条件,回表读取了完整的记录,判断完整记录是否满足Table Filter中的查询条件,同样的,若不满足,跳过当前记录,继续读取索引的下一条记录,若满足,则返回记录,此记录满足了where的所有条件,可以返回给前端用户。
源码分析
入口函数:
- sql_select.cc#handle_query
- 单一查询
- 设置limit
- prepare
- join或者子查询
- 单一查询
- optimize
- 单一查询 select-> optimize
- join或子查询 unit-> optimize
- 是否是explain语句
- 是 执行explain_query
- 否 是否是单一查询
- 是 select->join->exec()
- 否 unit->exec() connection_handler_per_thread.cc#do_command
sql_parse.cc#mysql_execute_command execute_sqlcom_select
收集一下问题:
- 查询优化器如何优化
- 联合索引和单列索引如何选择
- 两个单列索引是选择索引合并还是选一个选择性较大的,然后using where
- ICP