在开始之前,请先看下以下四条SQL,请问它们的数据逻辑是一样的吗,或者说它们的结果必然一样的吗?(这里不考虑SQL的效率,优化)
-- SQL1
select a.k1, a.v1, b.k2, b.v2
from A a
left join B b on a.k1=b.k2 and b.status = 0
where a.status = 0
-- SQL2
select a.k1, a.v1, b.k2, b.v2
from A a left join B b on a.k1 = b.k2
where a.status = 0 and b.status = 0
-- SQL3
select a.k1, a.v1, b.k2, b.v2
from A a
left join
(
select k2, v2 from B where status = 0
) b on a.k1 = b.k2
where a.status = 0
-- SQL4
select a.k1, a.v1, b.k2, b.v2
from
(
select k1, v1 from A where status = 0
) a
left join
(
select k2, v2 from B where status = 0
) b on a.k1 = b.k2
假设A,B表分别为以下数据
A | B | |||||
---|---|---|---|---|---|---|
k1 | v1 | status | k2 | v2 | status | |
1 | a1 | 0 | 1 | b1 | 0 | |
2 | a2 | 0 | 2 | b2 | 1 | |
3 | a3 | 0 | 2 | b22 | 0 | |
4 | a4 | 1 | 4 | b4 | 0 |
那么最后执行的结果如下,可以发现SQL1, SQL3,SQL4的逻辑是一样的,而SQL2的逻辑则不同,但实际情况下大家更有可能写成SQL2的形式
SQL1 | SQL2 | SQL3 | SQL4 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
k1 | v1 | k2 | v2 | k1 | v1 | k2 | v2 | k1 | v1 | k2 | v2 | k1 | v1 | k2 | v2 | |||
1 | a1 | 1 | b1 | 1 | a1 | 1 | b1 | 1 | a1 | 1 | b1 | 1 | a1 | 1 | b1 | |||
2 | a2 | 2 | b22 | 2 | a2 | 2 | b22 | 2 | a2 | 2 | b22 | 2 | a2 | 2 | b22 | |||
3 | a3 | 3 | a3 | 3 | a3 |
实际上仔细分析SQL的执行流程,不难就容易分析出来。简要概括起来,一条LEFT JOIN SQL的大致执行逻辑如下(此处不考虑group by, order by等语法,以及分布式表,驱动表where语句下推等情况)
此时我们先回头看下SQL1,他的临时表T1数据如下
k1 | v1 | a.status | k2 | v2 | b.status |
---|---|---|---|---|---|
1 | a1 | 0 | 1 | b1 | 0 |
2 | a2 | 0 | 2 | b22 | 0 |
3 | a3 | 0 | |||
4 | a4 | 1 | 4 | a4 | 0 |
因为在第2步只让b表中status = 0的记录参与关联,估临时表中b表的status都是0,而a的所有记录都保留,此时在T1上做3. WHERE a.status = 0的过滤,以及4. SELECT a.k1, a.v1, b.k2, b.v2的数据,就不难得出上述的最终答案了。
此时我们再回头来看SQL2,他的临时T2数据为
k1 | v1 | a.status | k2 | v2 | b.status |
---|---|---|---|---|---|
1 | a1 | 0 | 1 | b1 | 0 |
2 | a2 | 0 | 2 | b2 | 1 |
2 | a2 | 0 | 2 | b22 | 0 |
3 | a3 | 0 | |||
4 | a4 | 1 | 4 | a4 | 0 |
此时在T2上做3. WHERE a.status = 0 and b.status = 0的过滤,以及4. SELECT a.k1, a.v1, b.k2, b.v2的数据,同样不难得出上述的最终答案了。
SQL3, SQL4留给读者自行测试推导
这里就简要举些栗子