数据准备
1、设置参数: log_bin_trust_function_creators
select @@log_bin_trust_function_creators;
如果参数不是1 则需要设置参数:
set global log_bin_trust_function_creators=1; # 不加global只是当前窗口有效
2、建表
CREATE TABLE s1 (
id INT AUTO_INCREMENT,
key1 VARCHAR ( 100 ),
key2 INT,
key3 VARCHAR ( 100 ),
key_part1 VARCHAR ( 100 ),
key_part2 VARCHAR ( 100 ),
key_part3 VARCHAR ( 100 ),
common_field VARCHAR ( 100 ),
PRIMARY KEY ( id ),
INDEX idx_key1 ( key1 ),
UNIQUE INDEX idx_key2 ( key2 ),
INDEX idx_key3 ( key3 ),
INDEX idx_key_part ( key_part1, key_part2, key_part3 )
) ENGINE = INNODB CHARSET = utf8;
CREATE TABLE s2 (
id INT AUTO_INCREMENT,
key1 VARCHAR ( 100 ),
key2 INT,
key3 VARCHAR ( 100 ),
key_part1 VARCHAR ( 100 ),
key_part2 VARCHAR ( 100 ),
key_part3 VARCHAR ( 100 ),
common_field VARCHAR ( 100 ),
PRIMARY KEY ( id ),
INDEX idx_key1 ( key1 ),
UNIQUE INDEX idx_key2 ( key2 ),
INDEX idx_key3 ( key3 ),
INDEX idx_key_part ( key_part1, key_part2, key_part3 )
) ENGINE = INNODB CHARSET = utf8;
3、创建函数、创建存储过程、调用存储过程
CREATE FUNCTION rand_string1(n INT)
RETURNS VARCHAR(255) #该函数会返回一个字符串
BEGIN
DECLARE chars_str VARCHAR(100) DEFAULT
'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
DECLARE return_str VARCHAR(255) DEFAULT '';
DECLARE i INT DEFAULT 0;
WHILE i < n DO
SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
SET i = i + 1;
END WHILE;
RETURN return_str;
END;
CREATE PROCEDURE insert_s1 (
IN min_num INT ( 10 ),
IN max_num INT ( 10 )) BEGIN
DECLARE
i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET i = i + 1;
INSERT INTO s1
VALUES
(
( min_num + i ),
rand_string1 ( 6 ),
( min_num + 30 * i + 5 ),
rand_string1 ( 6 ),
rand_string1 ( 10 ),
rand_string1 ( 5 ),
rand_string1 ( 10 ),
rand_string1 ( 10 ));
UNTIL i = max_num
END REPEAT;
COMMIT;
END;
CREATE PROCEDURE insert_s2 (
IN min_num INT ( 10 ),
IN max_num INT ( 10 )) BEGIN
DECLARE
i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET i = i + 1;
INSERT INTO s2
VALUES
(
( min_num + i ),
rand_string1 ( 6 ),
( min_num + 30 * i + 5 ),
rand_string1 ( 6 ),
rand_string1 ( 10 ),
rand_string1 ( 5 ),
rand_string1 ( 10 ),
rand_string1 ( 10 ));
UNTIL i = max_num
END REPEAT;
COMMIT;
END;
CALL insert_s1(10001,10000);
CALL insert_s2(10001,10000);
EXPLAIN 使用
语法
EXPLAIN SELECT select_options
或者
DESCRIBE SELECT select_options
版本情况
MySQL 5.6.3以前只能 EXPLAIN SELECT ;MYSQL 5.6.3以后就可以 EXPLAIN SELECT,UPDATE,
DELETE。在5.7以前的版本中,想要显示 partitions 需要使用 explain partitions 命令;想要显示
filtered 需要使用 explain extended 命令。在5.7版本后,默认explain直接显示partitions和
filtered中的信息。
EXPLAIN各列作用
table
#1. table:表名
#查询的每一行记录都对应着一个单表
EXPLAIN SELECT * FROM s1;
#s1:驱动表 s2:被驱动表
EXPLAIN SELECT * FROM s1 INNER JOIN s2;
id
在一个大的查询语句中每个SELECT关键字都对应一个唯一的id
EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';
EXPLAIN SELECT * FROM s1 INNER JOIN s2
ON s1.key1 = s2.key1
WHERE s1.common_field = 'a';
EXPLAIN SELECT * FROM s1
WHERE key1 IN (SELECT key3 FROM s2);
SELECT * FROM s1 UNION SELECT * FROM s2;
EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';
EXPLAIN SELECT * FROM s1 INNER JOIN s2;
EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'a';
######查询优化器可能对涉及子查询的查询语句进行重写,转变为多表查询的操作########
EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key2 FROM s2 WHERE common_field = 'a');
#Union去重
EXPLAIN SELECT * FROM s1 UNION SELECT * FROM s2;
EXPLAIN SELECT * FROM s1 UNION ALL SELECT * FROM s2;
特殊说明:
union 由于需要去重,因此多了一个虚拟表 <union1,2>
union all 不需要去重,所以只有 1 2
小结:id如果相同,可以认为是一组,从上往下顺序执行
在所有组中,id值越大,优先级越高,越先执行
关注点:id号每个号码,表示一趟独立的查询, 一个sql的查询趟数越少越好
select_type
查询语句中不包含UNION
或者子查询的查询都算作是SIMPLE
类型
对于包含UNION
或者UNION ALL
或者子查询的大查询来说,它是由几个小查询组成的,其中最左边的那个查询的select_type
值就是PRIMARY
MySQL
选择使用临时表来完成UNION
查询的去重工作,针对该临时表的查询的select_type
就是UNION RESULT
…
type
执行计划的一条记录就代表着MySQL对某个表的 执行査询时的访问方法,又称访问类型
,其中的type 列就表面了这个访问方法是啥,是较为重要的一个指标。比如,看到type列的值是ref ,表明 MySQL 即将使用ref 访问方法来执行对 s1 表的查询。
完整的访问方法如下:system,const,eq_ref,ref,fulltext,ref_or_null,index_merge,unique_subquery,index_subquery,range ,index, ALL
system
当表中只有一条记录
并且该表使用的存储引擎的统计数据是精确的,比如MyISAM、Memory, 那么对该表的访问方法就是system
。
# 5. type:针对单表的访问方法
#当表中`只有一条记录`并且该表使用的存储引擎的统计数据是精确的,比如MyISAM、Memory,
#那么对该表的访问方法就是`system`。
CREATE TABLE t(i INT) ENGINE=MYISAM;
INSERT INTO t VALUES(1);
EXPLAIN SELECT * FROM t;
#换成InnoDB
CREATE TABLE tt(i INT) ENGINE=INNODB;
INSERT INTO tt VALUES(1);
EXPLAIN SELECT * FROM tt;
#当我们根据主键或者唯一二级索引列与常数进行等值匹配时,对单表的访问方法就是`const`
EXPLAIN SELECT * FROM s1 WHERE id = 10005;
EXPLAIN SELECT * FROM s1 WHERE key2 = 10066;
#在连接查询时,如果被驱动表是通过主键或者唯一二级索引列等值匹配的方式进行访问的
#(如果该主键或者唯一二级索引是联合索引的话,所有的索引列都必须进行等值比较),则
#对该被驱动表的访问方法就是`eq_ref`
EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.id = s2.id;
#当通过普通的二级索引列与常量进行等值匹配时来查询某个表,那么对该表的访问方法就可能是`ref`
EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';
#当对普通二级索引进行等值匹配查询,该索引列的值也可以是`NULL`值时,那么对该表的访问方法
#就可能是`ref_or_null`
EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key1 IS NULL;
#单表访问方法时在某些场景下可以使用`Intersection`、`Union`、
#`Sort-Union`这三种索引合并的方式来执行查询
EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key3 = 'a';
#`unique_subquery`是针对在一些包含`IN`子查询的查询语句中,如果查询优化器决定将`IN`子查询
#转换为`EXISTS`子查询,而且子查询可以使用到主键进行等值匹配的话,那么该子查询执行计划的`type`
#列的值就是`unique_subquery`
EXPLAIN SELECT * FROM s1
WHERE key2 IN (SELECT id FROM s2 WHERE s1.key1 = s2.key1) OR key3 = 'a';
#如果使用索引获取某些`范围区间`的记录,那么就可能使用到`range`访问方法
EXPLAIN SELECT * FROM s1 WHERE key1 IN ('a', 'b', 'c');
#同上
EXPLAIN SELECT * FROM s1 WHERE key1 > 'a' AND key1 < 'b';
#当我们可以使用索引覆盖,但需要扫描全部的索引记录时,该表的访问方法就是`index`
EXPLAIN SELECT key_part2 FROM s1 WHERE key_part3 = 'a';
#最熟悉的全表扫描
EXPLAIN SELECT * FROM s1;```