SQL全外连接两种写法结果差异:Azure SQL DB中为何结果不同?
两种FULL OUTER JOIN查询结果不同的原因分析
这个问题的核心差异,在于JOIN的ON条件是匹配条件而非过滤条件,和子查询先过滤再JOIN的逻辑完全不一样,咱们一步步拆解来看:
第一个查询的执行逻辑
SELECT * FROM Table_A FULL OUTER JOIN Table_B ON (Table_A.Col1 = Table_B.Col1 AND Table_B.iscurrent=1)
这里的Table_B.iscurrent=1是写在JOIN的ON子句里的,它的作用是判断两行是否能匹配合并,而不是过滤Table_B的行。也就是说:
- Table_B的所有行都会被保留在最终结果里:
- 只有当Table_B的行满足
iscurrent=1且Col1和Table_A的行匹配时,才会和Table_A的行合并成一行; - 对于Table_B中
iscurrent≠1的行,不管Col1是否和Table_A匹配,因为不满足ON的匹配条件,都会单独作为一行出现在结果中,对应的Table_A列全部用NULL填充;
- 只有当Table_B的行满足
- Table_A的所有行也会被保留:如果Table_A的行在Table_B中找不到
iscurrent=1且Col1匹配的行,对应的Table_B列用NULL填充。
第二个查询的执行逻辑
SELECT * FROM Table_A FULL OUTER JOIN (Select * FROM Table_B Where iscurrent=1) AS Table_B ON (Table_A.Col1 = Table_B.Col1)
这里先通过子查询(Select * FROM Table_B Where iscurrent=1)对Table_B做了提前过滤,只保留iscurrent=1的行生成临时表,再和Table_A做FULL OUTER JOIN。这意味着:
- Table_B中
iscurrent≠1的行直接被排除,根本不会进入JOIN环节,自然不会出现在最终结果里; - 后续的JOIN逻辑只在Table_A和过滤后的Table_B子集之间进行,匹配规则就是Col1相等,不匹配的行用NULL填充。
举个实际例子更直观
假设:
- Table_A有1行:
(Col1=1, ColA='A1') - Table_B有2行:
(Col1=1, ColB='B1', iscurrent=0)(Col1=2, ColB='B2', iscurrent=1)
第一个查询的结果会有3行:
(1, 'A1', NULL, NULL, NULL)→ Table_A的行,无匹配的Table_B行(因为Table_B中Col1=1的行iscurrent≠1)(NULL, NULL, 1, 'B1', 0)→ Table_B中iscurrent=0的行,无匹配的Table_A行(不满足ON条件)(NULL, NULL, 2, 'B2', 1)→ Table_B中iscurrent=1的行,无匹配的Table_A行
第二个查询的结果只有2行:
(1, 'A1', NULL, NULL, NULL)→ Table_A的行,无匹配的过滤后Table_B行(NULL, NULL, 2, 'B2', 1)→ 过滤后的Table_B行,无匹配的Table_A行
总结差异根源
两个查询的核心区别就是:
- 第一个查询会保留Table_B的所有行,哪怕是iscurrent≠1的行,只是这些行无法和Table_A的行匹配,会以NULL填充Table_A列的形式单独存在;
- 第二个查询会提前剔除Table_B中iscurrent≠1的行,最终结果里完全看不到这些行。
内容的提问来源于stack exchange,提问作者Michael Bremen
相关产品推荐
相关产品推荐

