ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

透彻研究通过explain命令得到的SQL执行计划(二)

2021-03-25 14:30:39  阅读:208  来源: 互联网

标签:index x1 explain 查询 透彻 SQL NULL type select


继续讲解不同SQL语句的执行计划长什么样子,来一起看一个包含子查询的SQL语句的执行计划:

EXPLAIN SELECT * FROM t1 WHERE x1 IN (SELECT x1 FROM t2) OR x3 = 'xxxx';

这个SQL就稍微有一点点的复杂了,因为主SELECT语句的WHERE筛选条件是依赖于一个子查询的,而且除此之外还有一个自己的WHERE筛选条件,那么它的执行计划长什么样子呢?

+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
 
| id | select_type | table | partitions | type  | possible_keys | key      | key_len | ref  | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
|  1 | PRIMARY     | t1    | NULL       | ALL   | index_x3      | NULL     | NULL    | NULL | 3457 |   100.00 | Using where |
 
|  2 | SUBQUERY    | t2    | NULL       | index | index_x1      | index_x1 | 507     | NULL | 4687 |   100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+

这个执行计划值得我们好好分析一下,首先,第一条执行计划的id是1,第二条执行计划的id是2,这是为什么?因为这个SQL里有两个SELECT,主查询SELECT的执行计划的id就是1,子查询SELECT的执行计划的id就是2。

其次,第一条执行计划里,select_type是PRIMARY,不是SIMPLE了,说明第一个执行计划的查询类型是主查询的意思,对主查询而言,它有一个where条件是x3=‘xxx’,所以它的possible_keys里包含了index_x3,就是x3字段的索引,但是它的key实际是NULL,而且type是ALL,所以说它最后没选择用x3字段的索引,而是选择了全表扫描。

这是为什么呢?其实很简单,可能它通过成本分析发现,使用x3字段的索引扫描xxx这个值,几乎就跟全表扫描差不多,可能x3这个字段的值几乎都是xxx,所以最后就选择还不如直接全表扫描呢。

接着第二条执行计划,它的select_type是SUBQUERY,也就是子查询,子查询针对的是t2这个表,当然子查询本身就是一个全表查询,但是对主查询而言,会使用x1 in 这个筛选条件,它这里type是index,说明使用了扫描index_x1这个x1字段的二级索引的方式,直接扫描x1字段的二级索引,来跟子查询的结果集做比对。

EXPLAIN SELECT * FROM t1  UNION SELECT * FROM t2

那么它的执行计划是什么样的呢?

+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
 
| id | select_type  | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
 
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
 
|  1 | PRIMARY      | t1         | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 3457 |   100.00 | NULL            |
 
|  2 | UNION        | t2         | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 4687 |   100.00 | NULL            |
 
| NULL | UNION RESULT | <union1,2> | NULL       | ALL  | NULL          | NULL | NULL    | NULL | NULL |     NULL | Using temporary |
 
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+

这个执行计划的第一条和第二条很好理解对吧?两个SELECT字句对应两个id,就是分别从t1表和t2表里进行全表扫描罢了。

接着第三条执行计划是什么呢?其实union字句默认的作用是把两个结果集合并起来还会进行去重,所以第三条执行计划干的是个去重的活儿。所以上面它的table是<union 1,2>,这就是一个临时表的表名,而且你看它的extra里,有一个using temporary,也就是使用临时表的意思,它就是把结果集放到临时表里进行去重的,就这么个意思。当然,如果你用的是union all,那么就不会进行去重了。

接着来看看这个select_type,select_type之前似乎看到过几种,有什么SIMPLE的,还有primary和subquery的,那么这些select_type都是什么意思?除此之外,还有哪几种select_type呢?

首先要告诉大家的是,一般如果单表查询或者是多表连接查询,其实它们的select_type都是SIMPLE,这个之前也都看到过了,意思就是简单的查询罢了。然后如果是union语句的话,就类似于select * from t1 union select * from t2,那么会对应两条执行计划,第一条执行计划是针对t1表的,select_type是PRIMARY,第二条执行计划是针对t2表的,select_type是UNION,这就是在出现union语句的时候,它们就不一样了。

在使用union语句的时候,会有第三条执行计划,这个第三条执行计划意思是针对两个查询的结果依托一个临时表进行去重,这个第三条执行计划的select_type就是union_result。

另外,之前还看到过,如果是在SQL里有子查询,类似于select * from t1 where x1 in (select x1 ffrom t2) or x3=‘xxx’,此时其实会有两条执行计划,第一条执行计划的select_type是PRIMARY,第二条执行计划的select_type是SUBQUERY,这个之前也看到过了。

那么现在来看一个稍微复杂一点的SQL语句:

EXPLAIN SELECT * FROM t1 WHERE x1 IN (SELECT x1 FROM t2 WHERE x1 = 'xxx' UNION SELECT x1 FROM t1 WHERE x1 = 'xxx');

这个SQL语句就稍微有点复杂了,因为它有一个外层查询,还有一个内层子查询,子查询里还有两个SELECT语句进行union操作,那么来看看它的执行计划会是什么样的呢?

+----+--------------------+------------+------------+------+---------------+----------+---------+-------+------+----------+--------------------------+
 
| id | select_type        | table      | partitions | type | possible_keys | key      | key_len | ref   | rows | filtered | Extra |
 
+----+--------------------+------------+------------+------+---------------+----------+---------+-------+------+----------+--------------------------+
 
|  1 | PRIMARY            | t1         | NULL       | ALL  | NULL          | NULL     | NULL    | NULL  | 3467 |   100.00 | Using where              |
 
|  2 | DEPENDENT SUBQUERY | t2         | NULL       | ref  | index_x1      | index_x1 | 899     | const |   59 |   100.00 | Using where; Using index |
 
|  3 | DEPENDENT UNION    | t1         | NULL       | ref  | index_x1      | index_x1 | 899     | const |    45 |   100.00 | Using where; Using index |
 
| NULL | UNION RESULT       | <union2,3> | NULL       | ALL  | NULL          | NULL     | NULL    | NULL  | NULL |     NULL | Using temporary |
 
+----+--------------------+------------+------------+------+---------------+----------+---------+-------+------+----------+--------------------------+

第一个执行计划一看就是针对t1表查询的那个外层循环,select_type就是PRIMARY,因为这里涉及到了子查询,所以外层查询的select_type一定是PRIMARY了。然后第二个执行计划是子查询里针对t2表的那个查询语句,它的select_type是DEPENDENT SUBQUERY,第三个执行计划是子查询里针对t1表的另外一个查询语句,select_type是DEPENDENT UNION,因为第三个执行计划是在执行union后的查询,第四个执行计划的select_type是UNION RESULT,因为在执行子查询里两个结果集的合并以及去重。

现在再来看一个更加复杂一点的SQL语句:

EXPLAIN SELECT * FROM (SELECT x1, count(*) as cnt FROM t1 GROUP BY x1) AS _t1 where cnt > 10;

这个SQL可有点麻烦了,FROM子句后跟了一个子查询,在子查询里是根据x1字段进行分组然后进行count聚合操作,也就是统计出来x1这个字段每个值的个数,然后在外层则是针对这个内层查询的结果集进行查询通过where条件来进行过滤,看看它的执行计划:

+----+-------------+------------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
 
| id | select_type | table      | partitions | type  | possible_keys | key      | key_len | ref  | rows | filtered | Extra |
 
+----+-------------+------------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
 
|  1 | PRIMARY     | <derived2> | NULL       | ALL   | NULL          | NULL     | NULL    | NULL | 3468 |    33.33 | Using where |
 
|  2 | DERIVED     | t1         | NULL       | index | index_x1      | index_x1 | 899     | NULL | 3568 |   100.00 | Using index |
 
+----+-------------+------------+------------+-------+---------------+----------+---------+------+------+----------+-------------+

上面的执行计划里,其实应该先看第二条执行计划,它说的是子查询里的那个语句的执行计划,它的select_type是derived,意思就是说,针对子查询执行后的结果集会物化为一个内部临时表,然后外层查询是针对这个临时的物化表执行的。

它这里执行分组聚合的时候,是使用的index_x1这个索引来进行的,type是index,意思就是直接扫描偶了index_x1这个索引树的所有叶子节点,把x1相同值的个数都统计出来就可以了。然后外层查询是第一个执行计划,select_type是PRIMARY,针对的table是,就是一个子查询结果集物化形成的临时表,它是直接针对这个物化临时表进行了全表扫描根据where条件进行筛选的

标签:index,x1,explain,查询,透彻,SQL,NULL,type,select
来源: https://blog.csdn.net/weixin_44583135/article/details/115207843

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有