Mysql 7种join连接方式深入分析

一,定义:
1)LEFT JOIN / LEFT OUTER JOIN:左外连接
左向外连接的结果集包括:LEFT OUTER子句中指定的左表的所有行,而不仅仅是连接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列均为空值。
2)RIGHT JOIN / RIGHT OUTER JOIN:右外连接
右向外连接是左向外联接的反向连接。将返回右表的所有行。如果右表的某行在左表中没有匹配行,则将为左表返回空值。
3)FULL JOIN / FULL OUTER JOIN:全外连接
全外连接返回左表和右表中的所有行。当某行在另一个表中没有匹配行时,则另一个表的选择列表列包含空值。如果表之间有匹配行,则整个结果集行包含基表的数据值


二,演示:
1,内连接
SELECT <list> FROM TABLEA A INNER JOIN TABLEB B ON A.key=B.key

2, 全外 连接 Mysql暂不支持
SELECT <list> FROM TABLEA A FULL OUTER JOIN TABLEB B ON A.key=B.key

3, 全外 连接-差集 Mysql暂不支持
SELECT <list> FROM TABLEA A FULL OUTER JOIN TABLEB B ON A.key=B.key WHERE A.key IS NULL OR B.key IS NULL

4,左(外)连接
SELECT <list> FROM TABLEA A LFET JOIN TABLEB B ON A.key=B.key

5,左(外)连接-左表无连接部分
SELECT <list> FROM TABLEA A LFET JOIN TABLEB B ON A.key=B.key WHERE B.key IS NULL

6,右(外)连接
SELECT <list> FROM TABLEA A RIGHT JOIN TABLEB B ON A.key=B.key

7,右(外)连接-右表无连接部分
SELECT <list> FROM TABLEA A RIGHT JOIN TABLEB B ON A.key=B.key WHERE A.key IS NULL

三,外连接消除:

把外连接变为内连接
A OUTER JOIN B
变形为
A JOIN B

外连接消除的意义:
1 查询优化器在处理外连接操作时所需执行的操作和时间多于内连接
2 外连接消除后,优化器在选择多表连接顺序时,可以有更多更灵活的选择,从而可以选择更好的表连接顺序,加快查询执行的速度
3 表的一些连接算法(如块嵌套连接和索引循环连接等)在将规模小的或筛选条件最严格的表作为“外表”(放在连接顺序的最前面,是多层循环体的外循环层),可以减少不必要的I/O开销,能加快算法执行的速度

外连接消除的 条件
WHERE子句中的条件 满足“ 空值拒绝 ” (又称为“reject-NULL”条件)。
WHERE条件 可以保证 从结果中 排除外连接右侧(右表)生成的值为NULL的行 (即条件确保应用在右表带有空值的列对象上时,条件不满足,条件的结果值为FLASE或UNKONOWEN,这样 右表就不会有值为NULL的行生成 ),所以能使该查询在语义上等 效于内连接
explain
SELECT * FROM X LEFT JOIN Y ON (X.X_num=Y.Y_num)
WHERE Y.Y_num IS NOT NULL ;

Mysql优化器对外连接的优化处理:
例1
explain extended select * from a left join b on a.id=b.uid where a.id=b.uid;
优化后:
select `test`.`a`.`id` AS `id`,`test`.`a`.`name` AS `name`,`test`.`a`.`age` AS `age`,`test`.`b`.`uid` AS `uid`,`test`.`b`.`score` AS `score`,`test`.`b`.`class` AS `class` from `test`.`a` join `test`.`b` where ( `test`.`b`.`uid` = `test`.`a`.`id` )

例2
explain extended select * from a left join b on a.id=b.uid where b.uid is not null;
优化后:
select `test`.`a`.`id` AS `id`,`test`.`a`.`name` AS `name`,`test`.`a`.`age` AS `age`,`test`.`b`.`uid` AS `uid`,`test`.`b`.`score` AS `score`,`test`.`b`.`class` AS `class` from `test`.`a` join `test`.`b` where (( `test`.`b`.`uid` = `test`.`a`.`id` ) and ( `test`.`a`.`id` is not null ))

例3
select * from a left join b on a.id=b.uid ;
select * from a left outer join b on a.id=b.uid where a.id=b.uid;
select * from a left join b on true where a.id=b.uid;
这2名sql,Mysql处理后,都优化成内连接
select `test`.`a`.`id` AS `id`,`test`.`a`.`name` AS `name`,`test`.`a`.`age` AS `age`,`test`.`b`.`uid` AS `uid`,`test`.`b`.`score` AS `score`,`test`.`b`.`class` AS `class` from `test`.`a` join `test`.`b` where (`test`.`b`.`uid` = `test`.`a`.`id`)


外连接消除示例1:---辨析ON和WHERE的差异
ON   t_1_id = t_2_id:
   t_1_id 和 t_2_id 进行连接
WHERE t_1_id = t_2_id:
   当t_1_id 和 t_2_id的值相等

外连接消除示例2:---深入理解WHERE条件对外连接优化的影响
外表 的索引列出现在WHERE子句中:
EXPLAIN EXTENDED SELECT * FROM t_1 LEFT JOIN t_2 ON t_1_id = t_2_id WHERE t_1_id>0 ;
优化后
select `tt2`.`t_1`.`t_1_id` AS `t_1_id`,
  `tt2`.`t_1`.`t_1_col_1` AS `t_1_col_1`,
  `tt2`.`t_1`.`t_1_col_2` AS `t_1_col_2`,`tt2`.`t_2`.`t_2_id` AS `t_2_id`,
  `tt2`.`t_2`.`t_2_col_1` AS `t_2_col_1`,`tt2`.`t_2`.`t_2_col_2` AS `t_2_col_2`
from `tt2`.`t_1` left join `tt2`.`t_2`on((`tt2`.`t_2`.`t_2_id` = `tt2`.`t_1`.`t_1_id`))
where (`tt2`.`t_1`.`t_1_id` > 0)

内表 的索引列出现在WHERE子句中:
EXPLAIN EXTENDED SELECT * FROM t_1 LEFT JOIN t_2 ON t_1_id = t_2_id WHERE t_2_id>0;
优化后
select `tt2`.`t_1`.`t_1_id` AS `t_1_id`,
  `tt2`.`t_1`.`t_1_col_1` AS `t_1_col_1`,`tt2`.
  `t_1`.`t_1_col_2` AS `t_1_col_2`,`tt2`.`t_2`.`t_2_id` AS `t_2_id`,
  `tt2`.`t_2`.`t_2_col_1` AS `t_2_col_1`,`tt2`.`t_2`.`t_2_col_2` AS `t_2_col_2`
from `tt2`.`t_1` join `tt2`.`t_2`
where ((`tt2`.`t_2`.`t_2_id` = `tt2`.`t_1`.`t_1_id`) and
  (`tt2`.`t_1`.`t_1_id` > 0) )

外连接消除总结:
1 注意外连接与内连接的语义差别
2 外连接优化的条件: 空值拒绝
3 外连接优化的本质:语义上是外连接,但 WHER条件 使得 外连接可以蜕化为内连接



四,连接消除:
   去掉不必要的连接对象,则减少了连接操作

连接消除的条件:
    无固定模式,具体问题具体处理

连接消除情况一:
   唯一键/主键作为连接条件,三表内连接可以去掉中间表
(中间表的列只作为连接条件)
CREATE TABLE A (a1 INT UNIQUE, a2 VARCHAR(9), a3 INT);
CREATE TABLE B (b1 INT UNIQUE, b2 VARCHAR(9), c2 INT);
CREATE TABLE C (c1 INT UNIQUE, c2 VARCHAR(9), c3 INT);
B的列在WHERE条件子句中只作为等值连接条件存在,则查询可以去掉对B的连接操作:
    SELECT A.*, C.* FROM A JOIN B ON (a1=b1) JOIN CON (b1=c1) ;
相当于:
    SELECT A.*, C.* FROM A JOIN C ON (a1= c1);

连接消除情况二:
   一些特殊形式,可以消除连接操作(可消除的表除了作为连接对象外,不出现在任何子句中)。
示例:
SELECT MAX(a1) FROM A, B;/* 在这样格式中的MIN、MAX函数操作可以消除连接,去掉B表不影响结果;其他聚集函数不可以 */
SELECT DISTINCT a3 FROM A, B; /* 对连接结果中的a3列执行去重操作*/
SELECT a1 FROM A, B GROUP BY a1;/* 对连接结果中的a1列执行分组操作 */

连接消除情况三:
   主外键关系的表进行的连接, 可消除主键表, 这不会影响对外键表的查询。

连接消除示例:---创建对象
创建表,命令如下:
CREATE TABLE B (b1 INT, b2 VARCHAR(2), PRIMARY KEY(b1));
CREATE TABLE A (a1 INT, a2 VARCHAR(2), FOREIGN KEY(a1) REFERENCES B(b1) ); /* A作为外键表参照主键表B */
CREATE TABLE C (c1 INT, c2 VARCHAR(2));
插入数据,命令如下:
INSERT INTO B VALUES(1, 'B1');
INSERT INTO B VALUES(2, 'B2');
INSERT INTO B VALUES(3, 'B3');
INSERT INTO A VALUES(1, 'A1');
INSERT INTO A VALUES(null, 'A2');
INSERT INTO A VALUES(3, 'A3');
INSERT INTO C VALUES(1, 'C1');
INSERT INTO C VALUES(2, 'C2');
INSERT INTO C VALUES(NULL, 'C3');

连接消除示例1:---对 主外键参照的表 进行 内连接 ,可以 消除主键表 MySQL不支持

第一条SQL:
三个表做内连接,但 目标列不包括主键表B的对象 ,主键表B只作为连接对象和连接条件存在;
mysql> SELECT A.*, C.* FROM A,B,C WHERE A.a1=B.b1 AND B.b1=C.c1; //主键表B作为连接对象和连接条件存在
+------+------+------+------+
| a1   | a2   | c1   | c2   |
+------+------+------+------+
|    1 | A1   |    1 | C1   |
+------+------+------+------+
1 row in set (0.05 sec)

连接消除示例1:---对 主外键参照的表 进行 内连接 ,可以 消除主键表 MySQL不支持
第一条SQL:
三个表做内连接,但 目标列不包括主键表B的对象 ,主键表B只作为连接对象和连接条件存在;
 
mysql> EXPLAIN EXTENDED SELECT A.*, C.* FROM A,B,C WHERE A.a1=B.b1 AND B.b1=C.c1;
+----+-------------+-------+--------+---------------+---------+---------+------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref      |
+----+-------------+-------+--------+---------------+---------+---------+------+
|  1 | SIMPLE      | C     | ALL    | NULL          | NULL    | NULL    | NULL     |
|  1 | SIMPLE      | B     | eq_ref | PRIMARY       | PRIMARY | 4       | test.C.c1|
|  1 | SIMPLE      | A     | ALL    | a1            | NULL    | NULL    | NULL     |
+----+-------------+-------+--------+---------------+---------+---------+------+


连接消除示例1:---对 主外键参照的表 进行 内连接 ,可以 消除主键表 MySQL不支持
第一条SQL:
三个表做内连接,但 目标列不包括主键表B的对象 ,主键表B只作为连接对象和连接条件存在;
 
被查询优化器处理后的语句为:
/* select#1 */ select `test`.`a`.`a1` AS `a1`, `test`.`a`.`a2` AS `a2`,
  `  test`.`c`.`c1` AS `c1`,  `test`.`c`.`c2` AS `c2`
from `test`.`a` join `test`.`b` join `test`.`c`
where ((`test`.`b`.`b1` = `test`.`c`.`c1`) and (`test`.`a`.`a1` = `test`.`c`.`c1`))


连接消除示例1:---对 主外键参照的表 进行 内连接 ,可以 消除主键表 MySQL不支持
第二条SQL:
只有表A和表C进行连接,但WHERE子句多一条判定条件“A.a1 IS NOT NULL”;
 
mysql> SELECT A.*, C.* FROM A,C WHERE A.a1=C.c1 AND A.a1 IS NOT NULL; //只有表A和C进行连接
+------+------+------+------+
| a1   | a2   | c1   | c2   |
+------+------+------+------+
|    1 | A1   |    1 | C1   |
+------+------+------+------+
1 row in set (0.01 sec)


连接消除示例1:---对 主外键参照的表 进行 内连接 ,可以 消除主键表 MySQL不支持
第二条SQL:
只有表A和表C进行连接,但WHERE子句多一条判定条件“A.a1 IS NOT NULL”;
 
查询执行计划如下:
mysql> EXPLAIN EXTENDED SELECT A.*, C.* FROM A,C WHERE A.a1=C.c1 AND A.a1 IS NOT NULL;
+----+-------------+-------+------+---------------+------+---------+-----------+
| id | select_type | table | type | possible_keys | key  | key_len | ref       |
+----+-------------+-------+------+---------------+------+---------+-----------+
|  1 | SIMPLE      | C     | ALL  | NULL          | NULL | NULL    | NULL      |
|  1 | SIMPLE      | A     | ref  | a1            | a1   | 5       | test.C.c1 |
+----+-------------+-------+------+---------------+------+---------+-----------+


连接消除示例1:---对 主外键参照的表 进行 内连接 ,可以 消除主键表 MySQL不支持
第二条SQL:
只有表A和表C进行连接,但WHERE子句多一条判定条件“A.a1 IS NOT NULL”;
 
被查询优化器处理后的语句为:
/* select#1 */ select `test`.`a`.`a1` AS `a1`,  `test`.`a`.`a2` AS `a2`,
    `test`.`c`.`c1` AS `c1`,  `test`.`c`.`c2` AS `c2`
from `test`.`a` join `test`.`c`
where ((`test`.`a`.`a1` = `test`.`c`.`c1`) and (`test`.`c`.`c1` is not null))


连接消除示例1:---对 主外键参照的表 进行 内连接 ,可以 消除主键表 MySQL不支持
第三条SQL:
只有表A和表C进行连接,但WHERE子句比第二条SQL的WHERE子句内容更为简单
 
mysql> SELECT A.*, C.* FROM A,C WHERE A.a1=C.c1;
+------+------+------+------+
| a1   | a2   | c1   | c2   |
+------+------+------+------+
|    1 | A1   |    1 | C1   |
+------+------+------+------+
1 row in set (0.00 sec)


连接消除示例1:---对 主外键参照的表 进行 内连接 ,可以 消除主键表
                    MySQL不支持
第三条SQL:
只有表A和表C进行连接,但WHERE子句比第二条SQL的WHERE子句内容更为简单
 
mysql> EXPLAIN EXTENDED SELECT A.*, C.* FROM A,C WHERE A.a1=C.c1;
+----+-------------+-------+------+---------------+------+---------+------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  |
+----+-------------+-------+------+---------------+------+---------+------+
|  1 | SIMPLE      | C     | ALL  | NULL          | NULL | NULL    | NULL |
|  1 | SIMPLE      | A     | ALL  | a1            | NULL | NULL    | NULL |
+----+-------------+-------+------+---------------+------+---------+------+


连接消除示例1:---对 主外键参照的表 进行 内连接 ,可以 消除主键表 MySQL不支持
第一条SQL:
EXPLAIN EXTENDED SELECT A.*, C.* FROM A,B,C WHERE A.a1=B.b1 AND B.b1=C.c1;
第二条SQL:
EXPLAIN EXTENDED SELECT A.*, C.* FROM A,C WHERE A.a1=C.c1 AND A.a1 IS NOT NULL;
第三条SQL:
EXPLAIN EXTENDED SELECT A.*, C.* FROM A,C WHERE A.a1=C.c1;


连接消除示例2:---对 主外键参照的表 进行 外连接 ,可以 消除主键表 MySQL不支持
表A外连接表B,然后连接表C,查询目标列没有表B的列,表B没有被消除,查询执行计划如下:
mysql> EXPLAIN EXTENDED SELECT A.*, C.* FROM A LEFT JOIN B ON (a1=b1) JOIN C ON (a1=c1);
+----+-------------+-------+--------+---------------+-----------------------------------------------
| id | select_type | table | type   | possible_keys | Extra     |
+----+-------------+-------+--------+---------------+-----------------------------------------------
|  1 | SIMPLE      | C     | ALL    | NULL          | NULL
|  1 | SIMPLE      | A     | ALL    | a1            | Using where; Using join buffer (Block Nested L
|  1 | SIMPLE      | B     | eq_ref | PRIMARY       | Using index
+----+-------------+-------+--------+---------------+-----------------------------------------------

连接消除示例2:---对 主外键参照的表 进行 外连接 ,可以 消除主键表 MySQL不支持
被查询优化器处理后的语句为:
/* select#1 */ select `test`.`a`.`a1` AS `a1`,  `test`.`a`.`a2` AS `a2`,
    `test`.`c`.`c1` AS `c1`,  `test`.`c`.`c2` AS `c2`
from `test`.`a` left join `test`.`b` on((`test`.`b`.`b1` = `test`.`c`.`c1`)) join `test`.`c`
where (`test`.`a`.`a1` = `test`.`c`.`c1`)


连接消除总结:
注意连接消除与外连接消除的技术差别
连接消除去掉的是被连接的某个对象
外连接消除去掉的是外连接的语义,变形为内连接




五,嵌套连接消除:

连接存在多个层次,用括号标识连接的优先次序。
嵌套连接消除,就是消除嵌套的连接层次,把多个层次的连接减少为较少层次的连接,尽量“扁平化”


嵌套连接消除示例:
创建表,命令如下:
CREATE TABLE B (b1 INT, b2 VARCHAR(9));
CREATE TABLE A (a1 INT, a2 VARCHAR(9));
CREATE TABLE C (c1 INT, c2 VARCHAR(9));
插入数据,命令如下:
INSERT INTO B VALUES(1, 'B1'), (NULL, 'B2'), (31, 'B31'), (32, 'B32'), (NULL, 'B4'),(5, 'B5'), (6, 'B6');
INSERT INTO A VALUES(1, 'A1'), (null, 'A2'), (NULL, 'A31'), (32, 'A32'), (4, 'A4'), (5, 'A5'), (NULL, 'A6');
INSERT INTO C VALUES(1, 'C1'), (NULL, 'C2'), (31, 'C31'), (NULL, 'C32'), (4, 'C4'), (NULL, 'C5'),(6, 'A6');


嵌套连接消除示例1:
SQL语句的语义是B和C先连接,然后再和A连接。查询执行计划如下:
mysql> EXPLAIN EXTENDED SELECT * FROM A JOIN (B JOIN C ON B.b1=C.c1) ON A.a1=B.b1
WHERE A.a1 > 1;
+----+-------------+-------+------------+------+------+----------+--------------------------------------------
| id | select_type | table | partitions | type | rows | filtered | Extra
+----+-------------+-------+------------+------+------+----------+--------------------------------------------
|  1 | SIMPLE      | A      | NULL       | ALL  |    7 |    33.33 | Using where        |
|  1 | SIMPLE      | B      | NULL       | ALL  |    7 |    14.29 | Using where; Using join buffer (Block Nested Loop) |
|  1 | SIMPLE      | C      | NULL       | ALL  |    7 |    14.29 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+------+----------+--------------------------------------------
3 rows in set, 2 warnings (0.02 sec)

嵌套连接消除示例1:
被查询优化器处理后的语句为:
/* select#1 */ select `test`.`a`.`a1` AS `a1`,`test`.`a`.`a2` AS `a2`,
  `test`.`b`.`b1` AS `b1`,`test`.`b`.`b2` AS `b2`,`test`.`c`.`c
1` AS `c1`,`test`.`c`.`c2` AS `c2`
from `test`.`a` join `test`.`b` join `test`.`c`
where ((`test`.`b`.`b1` = `test`.`a`.`a1`) and
  (`test`.`c`.`c1` = `test`.`a`.`a1`) and (`test`.`a`.`a1` > 1))


嵌套连接消除总结:
1 嵌套连接消除的连接的层次,这是一种连接的语义顺序的变化
2 连接消除,消掉的是一些被连接的对象
3 外连接消除,消掉的是外连接的语义,使得外连接变形为内连接

猜你喜欢

转载自blog.csdn.net/flyfreelyit/article/details/80136459