建表语句
CREATE TABLE IF NOT EXISTS `phone`(
`phoneid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`card` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY(`phoneid`)
)ENGINE = INNODB;
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
使用explain解读SQL
mysql> explain select * from class LEFT JOIN book on class.card=book.card LEFT JOIN phone on book.card = phone.card;
+----+-------------+-------+------------+-------+---------------+------+---------+-----------------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+------+---------+-----------------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | class | NULL | index | NULL | Y | 4 | NULL | 24 | 100.00 | Using index |
| 1 | SIMPLE | book | NULL | ref | Y | Y | 4 | test.class.card | 1 | 100.00 | Using index |
| 1 | SIMPLE | phone | NULL | ALL | NULL | NULL | NULL | NULL | 20 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+-------+---------------+------+---------+-----------------+------+----------+----------------------------------------------------+
3 rows in set, 1 warning (0.00 sec)
- 可以看到phone表的type为all, Extra中显示使用了连接缓冲区区。
创建phone表的card索引
alter table `phone` add index Z(`card`);
-
再用explain查看sql语句
mysql> explain select * from class LEFT JOIN book on class.card=book.card LEFT JOIN phone on book.card = phone.card; +----+-------------+-------+------------+-------+---------------+------+---------+-----------------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+------+---------+-----------------+------+----------+-------------+ | 1 | SIMPLE | class | NULL | index | NULL | Y | 4 | NULL | 24 | 100.00 | Using index | | 1 | SIMPLE | book | NULL | ref | Y | Y | 4 | test.class.card | 1 | 100.00 | Using index | | 1 | SIMPLE | phone | NULL | ref | Z | Z | 4 | test.book.card | 1 | 100.00 | Using index | +----+-------------+-------+------------+-------+---------------+------+---------+-----------------+------+----------+-------------+
- 注意rows: 扫描的行数
-
其实可以做更好的优化:
mysql> explain select * from phone LEFT JOIN book on phone.card=book.card LEFT JOIN class on book.card = class.card; +----+-------------+-------+------------+-------+---------------+------+---------+-----------------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+------+---------+-----------------+------+----------+-------------+ | 1 | SIMPLE | phone | NULL | index | NULL | Z | 4 | NULL | 20 | 100.00 | Using index | | 1 | SIMPLE | book | NULL | ref | Y | Y | 4 | test.phone.card | 1 | 100.00 | Using index | | 1 | SIMPLE | class | NULL | ref | Y | Y | 4 | test.book.card | 1 | 100.00 | Using index | +----+-------------+-------+------------+-------+---------------+------+---------+-----------------+------+----------+-------------+ 3 rows in set, 1 warning (0.00 sec)
- 思想: 用小表驱动大表, 能更好地减少行扫描次数。
-
结论:
- 索引最好设置在需要经常查询的字段中
- 尽可能减少Join语句中的NestedLoop的循环总数
- 永远用小结果集驱动大的结果集