create table t_user(
id int unsigned auto_increment primary key,
name varchar(10)
) engine = innodb default charset utf8;
insert into t_user (id,name) values (1,"test1"),(2,"test2"),(3,"test3");
create table t_user_level(
level_id int unsigned auto_increment primary key,
user_id int unsigned not null,
name varchar(10)
) engine = innodb default charset utf8;
insert into t_user_level (user_id, name) values (1, "青铜会员"),(2, "白银会员"),(3, "钻石会员")
create table t_level_log (
log_id int unsigned auto_increment primary key ,
level_id int unsigned not null
)engine = innodb default charset utf8;
insert into t_level_log (level_id) values (1),(2),(3)
简单查询,不包括子查询或者union操作
mysql> explain select * from t_user;
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | t_user | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | NULL |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
在未被SEMIJOIN的情况下
如果有子查询,那么最外层的查询被标记为PRIMARY
由于select name from t_user id 依赖于子查询的查询结果,所以该子查询被标记为 DEPENDENT SUBQUERY
使用max是防止查询优化器SEMIJOIN
mysql> explain select name from t_user where id in (select max(user_id) from t_user_level);
+----+--------------------+--------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+--------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | PRIMARY | t_user | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | Using where |
| 2 | DEPENDENT SUBQUERY | t_user_level | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | NULL |
+----+--------------------+--------------+------------+------+---------------+------+---------+------+------+----------+-------------+
被查询优化器SEMIJOIN,生成临时表
最后是临时表去和user join 得到的结果
mysql> explain select name from t_user where id in (select user_id from t_user_level);
+----+--------------+--------------+------------+--------+---------------+---------+---------+---------------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+--------------+------------+--------+---------------+---------+---------+---------------------+------+----------+-------+
| 1 | SIMPLE | <subquery2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | 100.00 | NULL |
| 1 | SIMPLE | t_user | NULL | eq_ref | PRIMARY | PRIMARY | 4 | <subquery2>.user_id | 1 | 100.00 | NULL |
| 2 | MATERIALIZED | t_user_level | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | NULL |
+----+--------------+--------------+------------+--------+---------------+---------+---------+---------------------+------+----------+-------+
不被最外层直接依赖的子查询
id 1 直接依赖于 id2
id 1 不直接依赖于 id3
id 2 依赖于 id3
mysql> explain select name from t_user where id in (select max(user_id) from t_user_level where level_id in (select min(level_id) from t_level_log));
+----+--------------------+--------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+--------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | PRIMARY | t_user | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | Using where |
| 2 | DEPENDENT SUBQUERY | t_user_level | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | Using where |
| 3 | SUBQUERY | t_level_log | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | NULL |
+----+--------------------+--------------+------------+------+---------------+------+---------+------+------+----------+-------------+
将上面语句修改下,去掉min,让查询优化器对其进行SEMIJOIN
t_level_log 生成临时表
最外层被标记为PRIMARY
id1 依赖于id2,但不是直接依赖,而是依赖于 id2 的 join 结果,故此id2被标记为 SUBQUERY 而不是 DEPENDENT SUBQUERY
mysql> explain select name from t_user where id in (select max(user_id) from t_user_level where level_id in (select level_id from t_level_log));
+----+--------------+--------------+------------+--------+---------------+---------+---------+----------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+--------------+------------+--------+---------------+---------+---------+----------------------+------+----------+-------------+
| 1 | PRIMARY | t_user | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | Using where |
| 2 | SUBQUERY | <subquery3> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | 100.00 | NULL |
| 2 | SUBQUERY | t_user_level | NULL | eq_ref | PRIMARY | PRIMARY | 4 | <subquery3>.level_id | 1 | 100.00 | NULL |
| 3 | MATERIALIZED | t_level_log | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | NULL |
+----+--------------+--------------+------------+--------+---------------+---------+---------+----------------------+------+----------+-------------+
使用union产生的结果集被标记为 UNION RESULT
mysql> explain select * from t_user union select * from t_user;
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| 1 | PRIMARY | t_user | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | NULL |
| 2 | UNION | t_user | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | NULL |
| NULL | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
使用union 或 union all 后的查询被标记union
将第2,3两个查询与1合并
1 被标记为PRIMARY
2,3 被标记为union
mysql> explain select name from t_user union all select name from t_user union all select name from t_user;
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | PRIMARY | t_user | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | NULL |
| 2 | UNION | t_user | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | NULL |
| 3 | UNION | t_user | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | NULL |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+
直接依赖 UNION 查询的结果
PRIMARY 直接依赖于 t1结果,t1标记为PRIMARY
由于t2的结果也直接被PRIMARY依赖,且是UNION,t2标记为 DEPENDENT UNION
mysql> explain select * from t_user where id in (select id from t_user as t1 union all select id from t_user as t2);
+----+--------------------+--------+------------+--------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+--------+------------+--------+---------------+---------+---------+------+------+----------+-------------+
| 1 | PRIMARY | t_user | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | Using where |
| 2 | DEPENDENT SUBQUERY | t1 | NULL | eq_ref | PRIMARY | PRIMARY | 4 | func | 1 | 100.00 | Using index |
| 3 | DEPENDENT UNION | t2 | NULL | eq_ref | PRIMARY | PRIMARY | 4 | func | 1 | 100.00 | Using index |
+----+--------------------+--------+------------+--------+---------------+---------+---------+------+------+----------+-------------+
3 rows in set, 1 warning (0.00 sec)
from 后的子查询被标记为DERIVED
第二个被标记为 SIMPLE 是因为经过优化器后变成
select * from t_user,from后面并不是子查询
mysql> explain select * from (select * from t_user as t1 union all select * from t_user as t2) as a;
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | PRIMARY | <derived2> | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100.00 | NULL |
| 2 | DERIVED | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | NULL |
| 3 | UNION | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | NULL |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------+
3 rows in set, 1 warning (0.00 sec)
mysql> explain select * from (select * from t_user) as b;
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | t_user | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | NULL |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+