今天我们动手练习下,两个表或多表结合查询的SQL语句是怎样优化的。
还是我们先创建两个表和准备一些数据
create table teacher(
tid int(4) primary key,
cid int(4) not null
);
insert teacher values(1,2);
insert teacher values(2,1);
insert teacher values(3,3);
create table course(
cid int(4) ,
cname varchar(20)
);
insert course values(1,'java');
insert course values(2,'javaweb');
insert course values(3,'spring');
下边我们来写一个连接的查询语句,并查看执行计划。
mysql> explain select * from teacher t left outer join course c on t.cid=c.cid where c.cname='java';
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+
| 1 | SIMPLE | c | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where |
| 1 | SIMPLE | t | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where; Using join buffer (hash join) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+
2 rows in set, 1 warning (0.00 sec)
从执行计划结果看到type是all,说明没有进行任何优化,而且出现了using join buffer,它是什么意思呢,mysql逻辑分层中有一个sql优化器,由于我们写的sql性能太差了,它为我们自动添加了连接缓存。
那我们怎样优化呢,就算加索引应该往哪个表加,记住下边的规则。
- 小表驱动大表,当编写sql语句时,先写小表,再写大表
- 索引加在经常使用的字段上
- 左连接给左表加索引,右连接给右表加索引
我们继续往下说,按照上边的规则,我们要加索引的话需要往左连接的表中追加,就是给teacher表的cid加索引后,在来看下执行计划。
alter table teacher add index index_cid(cid);
mysql> explain select * from teacher t left outer join course c on t.cid=c.cid where c.cname='java';
+----+-------------+-------+------------+------+---------------+-----------+---------+---------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-----------+---------+---------------------+------+----------+-------------+
| 1 | SIMPLE | c | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where |
| 1 | SIMPLE | t | NULL | ref | index_cid | index_cid | 4 | explain_test1.c.cid | 1 | 100.00 | Using index |
+----+-------------+-------+------------+------+---------------+-----------+---------+---------------------+------+----------+-------------+
2 rows in set, 1 warning (0.01 sec)
从这个执行计划结果来看,teacher表已经优化到type是ref级别,效率大大提升了,由于检索条件是按cname检索,我们来试试往cname上在加一个索引后,我们再来分析下执行计划。
alter table course add index index_cname(cname);
mysql> explain select * from teacher t left outer join course c on t.cid=c.cid where c.cname='java';
+----+-------------+-------+------------+------+---------------+-------------+---------+---------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-------------+---------+---------------------+------+----------+-------------+
| 1 | SIMPLE | c | NULL | ref | index_cname | index_cname | 83 | const | 1 | 100.00 | Using where |
| 1 | SIMPLE | t | NULL | ref | index_cid | index_cid | 4 | explain_test1.c.cid | 1 | 100.00 | Using index |
+----+-------------+-------+------------+------+---------------+-------------+---------+---------------------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
也同样,两张表已经提升到ref级别了,性能也大大增加了,这里我们的例子是两张表结合的优化,而三四张表结合的原理都是一样的,小表驱动大表,将索引加在经常使用的字段上。
在文章最后,我们已怎样操作索引字段的小技巧来结束今天的文章。
- 复合索引不要跨列或无序使用
- 尽量使用全索引匹配
- 不要在索引上进行任何操作,例如计算,函数,类型转换(显式或隐式)等
- 索引列不能使用不等于(!= ,<>)或者大于(>)或者小于(<)或者is null或者is not nul
- 索引优化时尽量达到using index
- 写like语句时不要以%开头
- 索引列不能使用or