Author: 陈江(恬泰)
JOIN 是关系数据库中最基本的操作之一。根据类型的不同,可以将 JOIN 分为 INNER JOIN、OUTER JOIN,OUTER JOIN 可以进一步分为 LEFT OUTER JOIN、RIGHT OUTER JOIN 和 FULL OUTER JOIN。 对于 INNER JOIN,MySQL 中的优化器已有的优化方式是通过 greedy search 找到较优的 JOIN 顺序以提升查询性能。但是如果可以通过一定规则在不影响查询结果的情况下将不必要的 JOIN 消除,那么则可以完全避免一些 JOIN 操作。 对于 OUTER JOIN,除了 FULL OUTER JOIN,其余的两种 OUTER JOIN 都不具备交换性,因此无法像 INNER JOIN 一样通过交换顺序获取更好的性能。MySQL 已有的一些规则可以通过把 OUTER JOIN 转化为 INNER JOIN,这样就可以获取 JOIN 的交换性,以搜索更好的 JOIN 顺序。但是, MySQL 已有的转换规则不是很全面,一些可以将 OUTER JOIN 优化为 INNER JOIN 的 case 并没有覆盖到。 本文主题是 JOIN 消除(JOIN Elimination),包括 INNER JOIN 的消除和 OUTER JOIN 的消除。我们为polardb新增了诺干条消除规则,做到应消尽消,大幅领先于其他厂商的mysql系产品。
连接(JOIN)消除已上线,在MySQL 8.0.2,且修订版本需为8.0.2.2.31中已灰度发布。 举个优化的例子,更加直观
SET SESSION join_elimination_mode = 'ON';
#####c表被完全消除掉了
EXPLAIN SELECT o.*, c.* FROM orders o LEFT JOIN customers c ON FALSE;
==》
select o.* ,NULL AS `customer_id`,NULL AS `customer_name` from `testdb`.`orders` `o`
更多样例,请详情参考https://help.aliyun.com/zh/polardb/polardb-for-mysql/user-guide/join-elimination
支持六种场景下的JOIN消除。
SELECT ..., ti1.*, ti2.*, ..., tin.*, ... FROM ... LEFT JOIN (ti1, ti2, ..., tin) ON FALSE
=>
SELECT ..., NULL, ..., NULL, ... FROM ...
SELECT to1.*, to2.*, ..., tom.* FROM to1, to2, ..., tom LEFT JOIN (ti1, ti2, ..., tin) ON cond_on WHERE cond_where ...
=>
SELECT to1.*, to2.*, ..., tom.* FROM to1, to2, ..., tom WHERE cond_where ...
CREATE TABLE t1 (uk int unique, ...);
基表JOIN基表
SELECT target.*, source.* FROM t1 as target JOIN t1 as source WHERE target.uk = source.uk;
=>
SELECT source.*, source.* FROM source WHERE source.uk = source.uk;
派生表JOIN派生表
SELECT target.*, source.* FROM (SELECT * FROM t1) target JOIN (SELECT * FROM t1 WHERE t1.a > 1) source WHERE target.uk = source.uk;
=>
SELECT source.*, source.* FROM (SELECT * FROM t1 WHERE t1.a > 1) source WHERE source.uk = source.uk;
基表JOIN派生表
SELECT target.*, source.* FROM t1 target JOIN (SELECT * FROM t1 WHERE t1.a > 1) source WHERE target.uk = source.uk;
=>
SELECT source.*, source.* FROM (SELECT * FROM t1 WHERE t1.a > 1) source WHERE source.uk = source.uk;
CREATE TABLE t1 (uk int unique, a int, b int);
有唯一列相等条件
SELECT source.* FROM t1 as source WHERE EXISTS (SELECT * FROM t1 as target WHERE source.uk = target.uk AND target.a > 1);
=>
SELECT source.* FROM t1 as source WHERE source.uk = source.uk AND source.a > 1;
无唯一列相等条件
SELECT source.* FROM t1 as source WHERE EXISTS (SELECT * FROM t1 as target WHERE source.a = target.a);
=>
SELECT source.* FROM t1 as source WHERE source.a = source.a;
CREATE TABLE t1 (a int primary key);
CREATE TABLE t2 (a int, foreign key (a) references t1(a));
SELECT t1.a, t2.a FROM t1, t2 WHERE t1.a = t2.a;
=>
SELECT t2.a, t2.a FROM t2 WHERE t2.a = t2.a;
CREATE TABLE t1 (a int primary key);
CREATE TABLE t2 (a int, foreign key (a) references t1(a));
SELECT t2.a FROM t2 WHERE EXISTS(SELECT * FROM t1 WHERE t1.a = t2.a);
=>
SELECT t2.a FROM t2 WHERE t2.a = t2.a;