模拟 FULL OUTER JOIN:LEFT+RIGHT JOIN 与交叉连接的 UNION 性能

Posted

技术标签:

【中文标题】模拟 FULL OUTER JOIN:LEFT+RIGHT JOIN 与交叉连接的 UNION 性能【英文标题】:Simulating FULL OUTER JOIN: Performance of UNION of LEFT+RIGHT JOIN vs cross join 【发布时间】:2018-10-21 12:13:38 【问题描述】:

Access / Jet 数据库引擎不支持FULL OUTER JOINs:

SELECT Table1.*, Table2.*
FROM Table1
FULL OUTER JOIN Table2 ON Table1.JoinField = Table2.JoinField

通常推荐的替代方法是UNIONLEFTRIGH JOINs 的结果;以下内容的一些变化:

SELECT Table1.*, Table2.*
FROM Table1
LEFT JOIN Table2 ON Table1.JoinField = Table2.JoinField

UNION ALL 
SELECT Table1.*, Table2.*
FROM Table1
RIGHT JOIN Table2 ON Table1.JoinField = Table2.JoinField
WHERE Table1.JoinField IS NULL

但是,是否也可以使用交叉连接?

SELECT Table1.*, Table2.*
FROM Table1, Table2
WHERE Table1.JoinField = Table2.JoinField
    OR Table1.JoinField IS NULL
    OR Table2.JoinField IS NULL

以这种方式使用交叉连接有任何性能损失或其他缺点吗?

【问题讨论】:

我建议创建两个表,用虚拟数据填充它们,并将 full join 结果与您的 , 替代结果进行比较。 @hvd 我觉得自己没有足够的能力创建具有足够相关的虚拟数据和结构的表来进行这样的实验。 我不认为 UNION 选项中的 where 子句是必要的......事实上,我觉得它可能会阻止你获得一些所需的结果......我错过了什么吗? @IsaacReefman 如果使用UNION ALL,那么INNER JOINed 记录会出现两次。一种替代方法是使用UNION,但前提是没有其他重复记录。 我相信所有的结果都是完全不同的。最后一个 sql 只会给你常见的行。第 2 个 sql 和第 1 个 sql 也会给出 dirr 结果。 【参考方案1】:

您的交叉连接根本不是FULL OUTER JOIN。这是一个内部连接,它也将 NULL 匹配到所有记录。

CROSS JOIN 中,一个表中的行总是与另一个表中的行匹配,而在FULL OUTER JOIN 中,有些行不匹配。

为了说明,我创建了一个小的sample(T-SQL,但这不相关)。可以看到返回了一个不相等的行。

但是,如果没有 Null 值,您可以使用 CROSS JOIN 来模拟 FULL OUTER JOIN,方法是附加 Null 行,使用 NOT EXISTS 和其他一些技巧。但是,您会发现这是一个非常复杂的解决方案,通常首选普通的UNION

SELECT *
FROM (SELECT * FROM #Table1 UNION ALL SELECT Null, Null) t1, (SELECT * FROM #Table2 UNION ALL SELECT Null, Null) t2
WHERE (t1.JoinField = t2.JoinField
OR (NOT EXISTS(SELECT 1 FROM #Table2 WHERE #Table2.JoinField = t1.JoinField) AND t1.JoinField Is Not Null AND t2.JoinField IS NULL)
OR (NOT EXISTS(SELECT 1 FROM #Table1 WHERE #Table1.JoinField = t2.JoinField) AND t2.JoinField Is Not Null AND t1.JoinField IS NULL))
AND (t1.JoinField Is Not Null Or t2.JoinField Is Not Null) 

(在链接的示例中,您可以看到它的实际效果)

【讨论】:

【参考方案2】:

由于我使用的是 Redshift,可能存在语法差异。

With a as
(
Select 1 id union all
Select 2 union all
Select 3 
)
, b as 
(
Select 2 d union all
Select 4 union all
Select 5 
)
Select a.*,b.* 
From a full join b on id=d

输出是

id  d
1   NULL
2   2
3   NULL
NULL    4
NULL    5

如果你运行

Select a.*,b.* 
from a 
left join b on id=d 
union all
Select  a.*,b.* 
from b 
left join a on d=id

你得到

id  d
1   NULL
2   2
3   NULL
2   2
NULL    4
NULL    5

但如果你只联合你会得到相同的结果。

【讨论】:

或者使用'WHERE'子句从UNION ALL、as in my question的第二个子查询中显式排除INNER JOIN记录。此外,使用 UNION 而不是 UNION ALL 假定没有其他重复结果。

以上是关于模拟 FULL OUTER JOIN:LEFT+RIGHT JOIN 与交叉连接的 UNION 性能的主要内容,如果未能解决你的问题,请参考以下文章

MYSQL FULL OUTER JOIN - 使用 LEFT-UNION-LEFT JOIN 时的所有 NULL 结果

SQL的JOIN语法解析(inner join, left join, right join, full outer join的区别)

Oracle表与表之间的连接方式(内连接:inner join 外连接 全连接: full outer join左连接:left outer join 右连接:right outer join(代码

SQL的JOIN语法解析(inner join, left join, right join, full outer join的区别)

SQL_连接(Join),内部连接(INNER JOIN),左连接(LEFT JOIN ),右连接(RIGHT JOIN)完整外部连接(FULL OUTER JOIN),自连接(Self JOIN)(

Spark SQL 中的 OUTER 和 FULL OUTER 有区别吗?