我们前边说过,内连接的驱动表和被驱动表的位置可以相互转换,而左(外) 连接和右(外)连接的驱动表和被驱动表是固定的。这就导致内连接可能通过优化表的连接顺序来降低整体的查询成本,而外连接却无法优化表的连接顺序。
    我们之前说过,外连接和内连接的本质区别就是:对于外连接的驱动表的记录来说,如果无法在被驱动表中找到匹配 ON 子句中的过滤条件的记录,那么该记录仍然会被加入到结果集中,对应的被驱动表记录的各个字段使用 NULL 值填充;而内连接的驱动表的记录如果无法在被驱动表中找到匹配 ON 子句中的过滤条件的记录,那么该记录会被舍弃。查询效果就是这样:

    1. SELECT * FROM e1 INNER JOIN e2 ON e1.m1 = e2.m2;

    image.png

    1. SELECT * FROM e1 LEFT JOIN e2 ON e1.m1 = e2.m2;

    image.png
    对于上边例子中的(左)外连接来说,由于驱动表 e1 中 m1=1, n1=’a’的记录无法在被驱动表 e2 中找到符合 ON 子句条件 e1.m1 = e2.m2 的记录,所以就直接把这条记录加入到结果集,对应的 e2 表的 m2 和 n2 列的值都设置为 NULL。
    因为凡是不符合 WHERE 子句中条件的记录都不会参与连接。只要我们在搜索条件中指定关于被驱动表相关列的值不为 NULL,那么外连接中在被驱动表中找不到符合ON 子句条件的驱动表记录也就被排除出最后的结果集了,也就是说: 在这种情况下:外连接和内连接也就没有什么区别了!比方说这个查询:

    1. mysql> SELECT * FROM e1 LEFT JOIN e2 ON e1.m1 = e2.m2 WHERE e2.n2 IS NOT NULL;

    image.png
    由于指定了被驱动表 e2 的 n2 列不允许为 NULL,所以上边的 e1 和 e2 表的左(外)连接查询和内连接查询是一样的。当然,我们也可以不用显式的指定被驱动表的某个列 IS NOT NULL,只要隐含的有这个意思就行了,比方说这样:

    1. mysql> SELECT * FROM e1 LEFT JOIN e2 ON e1.m1 = e2.m2 WHERE e2.m2 = 2;

    在这个例子中,我们在 WHERE 子句中指定了被驱动表 e2 的 m2 列等于 2, 也就相当于间接的指定了 m2 列不为 NULL 值,所以上边的这个左(外)连接查询其实和下边这个内连接查询是等价的:

    1. mysql> SELECT * FROM e1 INNER JOIN e2 ON e1.m1 = e2.m2 WHERE e2.m2 = 2;

    我们把这种在外连接查询中,指定的 WHERE 子句中包含被驱动表中的列不为 NULL 值的条件称之为空值拒绝(英文名:reject-NULL)。在被驱动表的 WHERE 子句符合空值拒绝的条件后,外连接和内连接可以相互转换。这种转换带来的好处就是查询优化器可以通过评估表的不同连接顺序的成本,选出成本最低的那种连接顺序来执行查询。