之前介绍了单表查询中的索引优化,本节将介绍关联查询中的索引优化。
案例将使用的数据表的创建如下:
# 1.创建图书类型表class
CREATE TABLE IF NOT EXISTS `class` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`card` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`id`)
);
# 2.创建图书信息表book
CREATE TABLE IF NOT EXISTS `book` (
`bookid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`card` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`bookid`)
);
1)开始explain分析
EXPLAIN SELECT * FROM book LEFT JOIN class ON class.card = book.card;
2)为book的card字段添加索引优化,并删除class中id小于5的记录:
ALTER TABLE `book` ADD INDEX Y (`card`);
Delete from class where id<5;
3)第2次explain
# class为主表,book表为被驱动表:索引在book表上
EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;
可以看到第二行的 type 变为了 ref,rows 也变成了优化比较明显。
这是由左连接特性决定的。LEFT JOIN 条件用于确定如何从右表搜索行,而左边的行一定都有,所以右边是我们的关键点,一定需要建立索引。
4)可以对比索引表放在LEFT JOIN左边主表的位置:
可以看出,虽使用了索引,但由于Left Join主表book的所有记录都需要查询,所以索引并没有发挥效用,rows依旧是20。
5)删除旧索引 + 新建class表新索引 + 使用inner join第3次explain
DROP INDEX Y ON book;
ALTER TABLE class ADD INDEX X (card);
EXPLAIN SELECT * FROM class Inner JOIN book ON class.card = book.card;
inner join 时,mysql会将有索引的表选为被驱动表
1)保证被驱动表的join字段已经被索引
2)left join 时,选择小表作为驱动表,大表作为被驱动表。
3)inner join 时,mysql会自己帮你把小结果集的表选为驱动表。
4)子查询尽量不要放在被驱动表,有可能使用不到索引。
5)能够直接多表关联的尽量直接关联,不用子查询。
1.尽量不要使用not in 或者 not exists
SELECT SQL_NO_CACHE age,count(*)
FROM emp a
WHERE id NOT IN (
SELECT ceo
FROM dept b2
WHERE ceo IS NOT NULL
)
Group By age
Having count(*)<10000
2.用left outer join on xxx is null 替代 NOT IN
EXPLAIN SELECT SQL_NO_CACHE age,count(*)
FROM emp a LEFT OUTER JOIN dept b ON a.id =b.ceo
WHERE b.ceo IS NULL
group by age
having count(*)<10000