MySQL的多表Join查询是会自动优化顺序的,本文将以一个实例讲述。
1、首先,编写一个建表脚本
[roo@localhost test]$ vi createA.sql
use test;
DROP PROCEDURE IF EXISTS PcreateA;
drop table if exists A;
drop table if exists B;
drop table if exists C;
create table A(
aid int(11) primary key,
title varchar(16),
content varchar(16),
uid int(11),
tid int(11)
);
create table B(
uid int(11) primary key,
uname varchar(16)
);
create table C(
tid int(11) primary key,
tname varchar(16),
age int(11)
);
DELIMITER //
CREATE procedure PcreateA() # 创建无参存储过程
BEGIN
DECLARE i INT default 0;
WHILE i<40000 DO # 结束循环的条件: 当i大于n时跳出while循环
INSERT INTO A VALUES(i,concat("title",i),'content', i%4000, i%100); # 往表添数据
SET i = i+1; # 循环一次,i加1
END WHILE; # 结束while循环
SELECT count(*) FROM A; # 查看表数据
set i=0;
WHILE i<8000 DO # 结束循环的条件: 当i大于n时跳出while循环
INSERT INTO B VALUES(i,concat('uname',i)); # 往表添数据
SET i = i+1; # 循环一次,i加1
END WHILE; # 结束while循环
SELECT count(*) FROM B; # 查看表数据
set i=0;
WHILE i<200 DO # 结束循环的条件: 当i大于n时跳出while循环
INSERT INTO C VALUES(i,concat('tname',i),i%10); # 往表添数据
SET i = i+1; # 循环一次,i加1
END WHILE; # 结束while循环
SELECT count(*) FROM C; # 查看表数据
END;
// # 结束定义语句
DELIMITER ; # 重新将分隔符设置为;
CALL PcreateA(); # 调用存储过程
2、编写查询脚本
[roo@localhost test]$ vi selectjoin.sql
use test;
explain select count(*) from (SELECT A.aid, A.title, B.uname, C.tname, C.age FROM A LEFT JOIN B ON A.uid=B.uid left JOIN C ON A.tid=C.tid where C.age<1) AA;
explain select count(*) from (select AC.aid, AC.title from (SELECT A.aid, A.title, A.uid, C.tname, C.age FROM A LEFT JOIN C ON A.tid=C.tid where C.age<1) AC LEFT JOIN B ON AC.uid=B.uid) BB;
3、依次执行脚本看结果
mysql> source /home/roo/test/createA.sql
略
(1)去掉selectjoin.sql中的explain,执行
Database changed
+----------+
| count(*) |
+----------+
| 4000 |
+----------+
1 row in set (0.08 sec)
+----------+
| count(*) |
+----------+
| 4000 |
+----------+
1 row in set (0.08 sec)
发现两者居然时间相同,难道不应该第二个“提前剪枝”的更优么??。。。。。
(2)执行explain查看执行计划
mysql> source /home/roo/test/selectjoin.sql
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+-------+----------+-------------+
| 1 | SIMPLE | A | NULL | ALL | NULL | NULL | NULL | NULL | 39303 | 100.00 | NULL |
| 1 | SIMPLE | C | NULL | eq_ref | PRIMARY | PRIMARY | 4 | test.A.tid | 1 | 33.33 | Using where |
| 1 | SIMPLE | B | NULL | eq_ref | PRIMARY | PRIMARY | 4 | test.A.uid | 1 | 100.00 | Using index |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+-------+----------+-------------+
3 rows in set, 1 warning (0.00 sec)
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+-------+----------+-------------+
| 1 | SIMPLE | A | NULL | ALL | NULL | NULL | NULL | NULL | 39303 | 100.00 | NULL |
| 1 | SIMPLE | C | NULL | eq_ref | PRIMARY | PRIMARY | 4 | test.A.tid | 1 | 33.33 | Using where |
| 1 | SIMPLE | B | NULL | eq_ref | PRIMARY | PRIMARY | 4 | test.A.uid | 1 | 100.00 | Using index |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+-------+----------+-------------+
3 rows in set, 1 warning (0.00 sec)
发现两者执行计划相同。
4、结果分析
MySQL执行语句过程中涉及到两大流程:优化器和执行器。其中优化器最主要的任务,是选择索引和在多表连接时选择连接顺序。
join顺序的选择会影响执行性能,确定join执行顺序就需要估算所有join操作的代价。
默认配置下MySQL会估算所有可能的组合,由于MySQL里限制一个查询的join表数目上限为61,所以MySQL有一个参数叫做optimizer_search_depth,默认为62,限制优化器的深度。
加入有20张表join查询,如optimizer_search_depth为4,则评估次数为20*19*18*17。
mysql> show variables like '%optimizer_search_depth%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| optimizer_search_depth | 62 |
+------------------------+-------+
在本例中,MySQL优化器执行了优化,所以两者都选取了最优的顺序。