为何Inner Join会覆盖之前的Outer Join?SQL执行逻辑解析
嘿,这个问题真的问到点子上了——很多人刚接触多表连接时都会踩这个坑,核心是没搞懂SQL引擎组合连接时的逻辑执行顺序,我给你一步步拆解清楚:
第一步:先执行A LEFT OUTER JOIN B
SQL里FROM子句的连接是按从左到右的逻辑顺序依次生成中间结果集的(虽然数据库优化器可能调整物理执行顺序,但逻辑上是这个流程)。
当你执行A LEFT OUTER JOIN B ON B.ID = A.ID时,得到的中间结果集包含:
- A表的所有行
- 匹配上B表的行,会带上对应的B列值
- 没匹配上B表的A行,B列全为
NULL
举个简单例子:
假设A有ID 1、2、3;B有ID 1、2
这一步的结果是:
| A.ID | B.ID |
|---|---|
| 1 | 1 |
| 2 | 2 |
| 3 | NULL |
第二步:用中间结果集执行INNER JOIN C
接下来,SQL引擎会拿上面这个中间结果集,去和C表做INNER JOIN C ON C.ID = B.ID。
这里的关键是:内连(INNER JOIN)要求两边的匹配条件必须成立,而NULL和任何值的比较结果都是不成立的(包括NULL和NULL比较)。
所以刚才中间结果里的第三行(A.ID=3,B.ID=NULL),因为C.ID = NULL永远不成立,会被内连直接过滤掉;而第二行(B.ID=2)如果C表里没有ID=2的行,也会被过滤。
还是用上面的例子,假设C只有ID=1:
这一步内连后的结果就只剩:
| A.ID | B.ID | C.ID |
|---|---|---|
| 1 | 1 | 1 |
最后你选A.*,就只能拿到A.ID=1的行——这看起来就像三个表全做了内连,但本质是左连后的NULL行被后续的内连过滤了。
怎么保留A的所有行?
如果你的需求是保留A的所有行,同时仅在B存在时关联C,有两种写法:
把第二个内连改成左连:
SELECT A.* FROM A LEFT OUTER JOIN B ON B.ID = A.ID LEFT OUTER JOIN C ON C.ID = B.ID这样即使B.ID是NULL,或者C没有匹配的行,A的行都会保留。
先让B和C做内连,再和A左连(用括号明确优先级):
SELECT A.* FROM A LEFT OUTER JOIN (B INNER JOIN C ON C.ID = B.ID) ON B.ID = A.ID这种写法会先得到B和C的内连结果,再让A左连这个结果,同样能保留A的所有行。
内容的提问来源于stack exchange,提问作者ismetguzelgun

