连接查询、联合查询、子查询:
查看表结构:
show create student;
desc student;
增删索引:
增加主键:alter table class add constraint primary key cid_pk(cid);
删除主键:alter table class drop primary key;
增加索引:alter table class add index cid_idx(cid); 或者 create index dexc_idx on class(cdesc);
删除索引:drop index sid on student;
查看表的索引:show index from student;
修改表的字符编码:alter table student charset = utf8mb4;
修改字段类型:alter table student modify name char(30) not null;
create table class(
cid int(10),
cdesc varchar(20)
);
create table student(
sid int(10),
name varchar(20),
age int(3),
cid int(10)
);
create table teacher(
tid int(10),
name varchar(20),
cid int(10)
);
insert into class values(1, "PHP"),(2, "Java"),(3, "C++"),(4,"SQL");
insert into student values(1, "s1",16,1),(2, "s2",17,2),(3, "s3",18,3),(4,"s4",19,4),(5, "s5",18,3),(6,"s6",19,4);
insert into teacher values(1, "t1",1),(2, "t2",2),(3, "t3",3),(4,"t4",4);
alter table student add constraint sid_pk primary key(sid);
MySQL 中,每条 SQL 语句的解析都是按照指定的顺序来的,而不是从左到右,顺序为:
from -> on -> join -> where -> group by -> having -> select distinct -> order by -> limit
例如,对于下面一条 SQL:
select distinct sid from student s left join class c on s.cid = c.cid where s.age = 19 group by name having sid > 4 order by sid desc limit 5;
MySQL 解析过程为:
官方文档:https://dev.mysql.com/doc/refman/5.7/en/explain-output.html
mysql> explain select * from s2;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | s2 | ALL | NULL | NULL | NULL | NULL | 64 | |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
1 row in set (0.00 sec)
id:查询序号
select_type:查询类型
table:表名
type:索引命中类型
possible_keys:可利用的索引
key:实际利用的索引
key_len:索引长度
ref:引用
rows:扫描的数据行数
Extra:附加信息
如果 SQL 中有子查询,则每个子查询会对应一个 id,且最内层子查询的 id 最大,最大的 id 最先执行。
select t.* from class c,student s,teacher t where s.cid = c.cid and t.cid = c.cid and (s.id = 1 or s.name = ‘s2’);
如果 SQL 中没有子查询,则 id 这一列都相同,从上往下逐行执行。
select s.name from (select * from student where sid < 5) s;
,下图中 table 列的 derived2 就是衍生表,数字2表示的就是id列中的值select * from (select * from student where sid < 2 union select * from student where sid > 2) s;
explain select * from student where sid in (select sid from student where sid > 3);
explain select * from student where sid in (select sid from student where sid < 2 union select sid from student where sid > 2);
mysql> explain select * from student;
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | student | ALL | NULL | NULL | NULL | NULL | 6 | |
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
1 row in set (0.00 sec)
mysql> explain select * from student where sid < 2 union select * from student where sid > 2;
+----+--------------+------------+-------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------+------------+-------+---------------+------+---------+------+------+-------------+
| 1 | PRIMARY | student | range | sid | sid | 5 | NULL | 1 | Using where |
| 2 | UNION | student | ALL | sid | NULL | NULL | NULL | 6 | Using where |
| NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | |
+----+--------------+------------+-------+---------------+------+---------+------+------+-------------+
3 rows in set (0.00 sec)
mysql> explain select * from student where sid in (select sid from student where sid > 3);
+----+--------------------+---------+----------------+---------------+------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+---------+----------------+---------------+------+---------+------+------+--------------------------+
| 1 | PRIMARY | student | ALL | NULL | NULL | NULL | NULL | 6 | Using where |
| 2 | DEPENDENT SUBQUERY | student | index_subquery | sid | sid | 5 | func | 1 | Using index; Using where |
+----+--------------------+---------+----------------+---------------+------+---------+------+------+--------------------------+
2 rows in set (0.02 sec)
mysql> explain select * from student where sid > (select sid from student where name = 's3');
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
| 1 | PRIMARY | student | ALL | sid | NULL | NULL | NULL | 6 | Using where |
| 2 | SUBQUERY | student | ALL | NULL | NULL | NULL | NULL | 6 | Using where |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
2 rows in set (0.00 sec)
mysql> explain select * from student where sid in (select sid from student where sid < 2 union select sid from student wher
+----+--------------------+------------+------+---------------+------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+------------+------+---------------+------+---------+------+------+--------------------------+
| 1 | PRIMARY | student | ALL | NULL | NULL | NULL | NULL | 6 | Using where |
| 2 | DEPENDENT SUBQUERY | student | ref | sid | sid | 5 | func | 1 | Using where; Using index |
| 3 | DEPENDENT UNION | student | ref | sid | sid | 5 | func | 1 | Using where; Using index |
| NULL | UNION RESULT | <union2,3> | ALL | NULL | NULL | NULL | NULL | NULL | |
+----+--------------------+------------+------+---------------+------+---------+------+------+--------------------------+
4 rows in set (0.00 sec)
mysql> explain select s.name from (select * from student where sid < 5) s;
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 4 | |
| 2 | DERIVED | student | ALL | NULL | NULL | NULL | NULL | 6 | Using where |
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
2 rows in set (0.00 sec)
mysql> explain select * from (select * from student where sid < 2 union select * from student where sid > 2) s;
+----+--------------+------------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------+------------+------+---------------+------+---------+------+------+-------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 5 | |
| 2 | DERIVED | student | ALL | NULL | NULL | NULL | NULL | 6 | Using where |
| 3 | UNION | student | ALL | NULL | NULL | NULL | NULL | 6 | Using where |
| NULL | UNION RESULT | <union2,3> | ALL | NULL | NULL | NULL | NULL | NULL | |
+----+--------------+------------+------+---------------+------+---------+------+------+-------------+
没有索引或没有用到索引时,需要全量扫描所有数据。命中索引时,有各种不同的效果差异。
MySQL 中的索引,效果由好到差排序为:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
一般来说,得保证查询至少达到 range 级别,最好能达到 ref。
要查询的表中(主表或衍生表)仅有一行数据。
mysql> select * from s1;
+-----+------+------+------+
| sid | name | age | cid |
+-----+------+------+------+
| 1 | s1 | 22 | 1 |
+-----+------+------+------+
1 row in set (0.00 sec)
mysql> explain select * from (select * from s1) s where sid = 1;
+----+-------------+------------+--------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+---------------+------+---------+------+------+-------+
| 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | |
| 2 | DERIVED | s1 | ALL | NULL | NULL | NULL | NULL | 1 | |
+----+-------------+------------+--------+---------------+------+---------+------+------+-------+
2 rows in set (0.00 sec)
用主键或唯一键做 where 条件。
mysql> show create table student\G
*************************** 1. row ***************************
Table: student
Create Table: CREATE TABLE `student` (
`sid` int(10) NOT NULL DEFAULT '0',
`name` varchar(20) DEFAULT NULL,
`age` int(3) DEFAULT NULL,
`cid` int(10) DEFAULT NULL,
PRIMARY KEY (`sid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
mysql> explain select * from student where sid = 3;
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------+
| 1 | SIMPLE | student | const | PRIMARY | PRIMARY | 4 | const | 1 | |
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------+
1 row in set (0.00 sec)
如果用主键做关联条件,效果更好:
mysql> alter table class add constraint primary key cid_pk(cid);
Query OK, 4 rows affected (0.23 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> explain select * from student s left join class c on s.cid = c.cid where s.sid = 3;
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| 1 | SIMPLE | s | const | PRIMARY | PRIMARY | 4 | const | 1 | |
| 1 | SIMPLE | c | const | PRIMARY | PRIMARY | 4 | const | 1 | |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
2 rows in set (0.00 sec)
对于非唯一索引,用作 where 条件时,可能命中多行,此时 type 类型是 ref。
mysql> alter table student add index name_idx(name);
mysql> explain select * from student where name = 's3';
+----+-------------+---------+------+---------------+----------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+----------+---------+-------+------+-------------+
| 1 | SIMPLE | student | ref | name_idx | name_idx | 23 | const | 1 | Using where |
+----+-------------+---------+------+---------------+----------+---------+-------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from student where name like 's3';
+----+-------------+---------+-------+---------------+----------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+----------+---------+------+------+-------------+
| 1 | SIMPLE | student | range | name_idx | name_idx | 23 | NULL | 1 | Using where |
+----+-------------+---------+-------+---------------+----------+---------+------+------+-------------+
1 row in set (0.00 sec)
全量扫描索引数据。
mysql> explain select sid from student;
+----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | student | index | NULL | PRIMARY | 4 | NULL | 6 | Using index |
+----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.00 sec)
全量扫描表中的数据。
mysql> explain select * from student;
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | student | ALL | NULL | NULL | NULL | NULL | 6 | |
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
1 row in set (0.00 sec)
MySQL 优化器判断可能会用到的索引,实际未必使用。
实际利用的索引。NULL 时没有用到索引。
where 条件中,索引用到的字节数 。通常用来判断复合索引命中了几个字段。MySQL 中,对于字符串,utf8 每个字符占 3 个字节,utf8_mb4 每个字符占 4 个字节。
首先创建测试表和测试数据:
create table filesort (
f1 int(10),
f2 int(10),
f3 int(10),
f4 int(10),
index idx_f4(f4),
index idx_f1_f2_f3(f1, f2, f3)
);
insert into filesort values(1, 2, 3, 4),(11, 12, 13, 14), (21, 22, 23, 24);
下面要查询的数据和 where 条件在同一个索引中,需要扫描全量索引数据。同时,索引字段都可以为 null,所以 key_len = (4 + 1) * 3 = 15:
mysql> explain select f2 from filesort where f3 = 3;
+----+-------------+----------+-------+---------------+--------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+---------------+--------------+---------+------+------+--------------------------+
| 1 | SIMPLE | filesort | index | NULL | idx_f1_f2_f3 | 15 | NULL | 3 | Using where; Using index |
+----+-------------+----------+-------+---------------+--------------+---------+------+------+--------------------------+
1 row in set (0.00 sec)
下面只用到了符合索引的第一个字段:
mysql> explain select f2 from filesort where f1 = 3;
+----+-------------+----------+------+---------------+--------------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+--------------+---------+-------+------+--------------------------+
| 1 | SIMPLE | filesort | ref | idx_f1_f2_f3 | idx_f1_f2_f3 | 5 | const | 1 | Using where; Using index |
+----+-------------+----------+------+---------------+--------------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)
对于 char 和 varchar 类型,以及是否允许 null,示例如下:
mysql> alter table filesort modify f4 varchar(30);
Query OK, 3 rows affected (2.24 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> explain select f2,f4 from filesort where f4 = '';
+----+-------------+----------+------+---------------+--------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+--------+---------+-------+------+-------------+
| 1 | SIMPLE | filesort | ref | idx_f4 | idx_f4 | 93 | const | 1 | Using where |
+----+-------------+----------+------+---------------+--------+---------+-------+------+-------------+
1 row in set (0.00 sec)
mysql> alter table filesort modify f4 char(30) not null;
Query OK, 3 rows affected (2.03 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> explain select f2,f4 from filesort where f4 = '';
+----+-------------+----------+------+---------------+--------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+--------+---------+-------+------+-------------+
| 1 | SIMPLE | filesort | ref | idx_f4 | idx_f4 | 90 | const | 1 | Using where |
+----+-------------+----------+------+---------------+--------+---------+-------+------+-------------+
1 row in set (0.00 sec)
utf8mb4 编码格式:
mysql> alter table filesort charset = utf8mb4;
Query OK, 3 rows affected (2.08 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> alter table filesort modify f4 char(30) not null;
Query OK, 3 rows affected (0.25 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> explain select f2,f4 from filesort where f4 = '';
+----+-------------+----------+------+---------------+--------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+--------+---------+-------+------+-------------+
| 1 | SIMPLE | filesort | ref | idx_f4 | idx_f4 | 120 | const | 1 | Using where |
+----+-------------+----------+------+---------------+--------+---------+-------+------+-------------+
1 row in set (0.00 sec)
哪些列或常量被用于查找索引列上的值。例如 where t1.id = t2.key
关联其他表的索引,where id = 3
用到了 const 常量。
mysql> explain select * from student where sid = 3;
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------+
| 1 | SIMPLE | student | const | PRIMARY | PRIMARY | 4 | const | 1 | |
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------+
1 row in set (0.00 sec)
mysql> alter table class add index idx_cid(cid);
Query OK, 0 rows affected (1.97 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> explain select * from student s,class c where s.cid = c.cid;
+----+-------------+-------+------+---------------+---------+---------+------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+---------+---------+------------+------+-------------+
| 1 | SIMPLE | s | ALL | NULL | NULL | NULL | NULL | 6 | |
| 1 | SIMPLE | c | ref | idx_cid | idx_cid | 5 | test.s.cid | 1 | Using where |
+----+-------------+-------+------+---------------+---------+---------+------------+------+-------------+
2 rows in set (0.00 sec)
扫描的数据行数。估计值。
where f1 = 3 and f1 = 4
。使用无索引的字段、不在 where 条件中的有索引字段、有组合索引但是没有命中,这三种情况下进行 order by 排序时,会多一次排序操作。首先创建测试表和测试数据:
create table filesort (
f1 int(10),
f2 int(10),
f3 int(10),
f4 int(10),
index idx_f4(f4),
index idx_f1_f2_f3(f1, f2, f3)
);
insert into filesort values(1, 2, 3, 4),(11, 12, 13, 14), (21, 22, 23, 24);
对于单列索引,如果 order by 的字段已经出现在 where 条件中,则不会出现 filesort:
mysql> explain select * from filesort where f4 = 1 order by f4;
+----+-------------+----------+------+---------------+--------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+--------+---------+-------+------+-------------+
| 1 | SIMPLE | filesort | ref | idx_f4 | idx_f4 | 5 | const | 1 | Using where |
+----+-------------+----------+------+---------------+--------+---------+-------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from filesort where f4 = 1 order by f3;
+----+-------------+----------+------+---------------+--------+---------+-------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+--------+---------+-------+------+-----------------------------+
| 1 | SIMPLE | filesort | ref | idx_f4 | idx_f4 | 5 | const | 1 | Using where; Using filesort |
+----+-------------+----------+------+---------------+--------+---------+-------+------+-----------------------------+
1 row in set (0.00 sec)
对于组合索引,则必须匹配组合索引的前缀,否则还是会出现 filesort:
mysql> explain select * from filesort where f1 = 1 and f2 = 2 order by f3;
+----+-------------+----------+------+---------------+--------------+---------+-------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+--------------+---------+-------------+------+-------------+
| 1 | SIMPLE | filesort | ref | idx_f1_f2_f3 | idx_f1_f2_f3 | 10 | const,const | 1 | Using where |
+----+-------------+----------+------+---------------+--------------+---------+-------------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from filesort where f1 = 1 order by f3;
+----+-------------+----------+------+---------------+--------------+---------+-------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+--------------+---------+-------+------+-----------------------------+
| 1 | SIMPLE | filesort | ref | idx_f1_f2_f3 | idx_f1_f2_f3 | 5 | const | 1 | Using where; Using filesort |
+----+-------------+----------+------+---------------+--------------+---------+-------+------+-----------------------------+
1 row in set (0.00 sec)
要查询的数据跟 where 条件命中索引,效率高:
mysql> explain select f2 from filesort where f3 = 3;
+----+-------------+----------+-------+---------------+--------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+---------------+--------------+---------+------+------+--------------------------+
| 1 | SIMPLE | filesort | index | NULL | idx_f1_f2_f3 | 15 | NULL | 3 | Using where; Using index |
+----+-------------+----------+-------+---------------+--------------+---------+------+------+--------------------------+
1 row in set (0.00 sec)
mysql> explain select f2 from filesort where f1 = 3;
+----+-------------+----------+------+---------------+--------------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+--------------+---------+-------+------+--------------------------+
| 1 | SIMPLE | filesort | ref | idx_f1_f2_f3 | idx_f1_f2_f3 | 5 | const | 1 | Using where; Using index |
+----+-------------+----------+------+---------------+--------------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)