如何在MySQL中进行完全外部连接?

How can I do a FULL OUTER JOIN in MySQL?

提问人:Spencer 提问时间:1/26/2011 最后编辑:Peter MortensenSpencer 更新时间:8/30/2022 访问量:1072158

问:

我想在MySQL中做一个完整的外部连接。这可能吗?MySQL是否支持完全外部连接

sql mysql 连接 outer-join full-outer-join

评论

4赞 Joe Stefanelli 1/26/2011
MySQL完全外部连接语法错误的可能重复项
4赞 philipxy 8/12/2018
当心这里的答案。SQL 标准说,完全联接是行上的内部联接,所有不匹配的左表行由 null 扩展,所有右表行由 null 扩展。这里的大多数答案都是错误的(见评论),而那些没有错的答案并不能处理一般情况。尽管有很多(不合理的)赞成票。(见我的回答。
0赞 Jairo Lozano 4/13/2019
当您尝试通过非主键/分组列进行联接时,该怎么办?就像我有一个查询每个状态的销售“状态”,“销售”和另一个每个状态的费用“状态”,“费用”,两个查询都使用group by(“state”)。当我在两个查询之间进行左右连接之间的联合时,我得到几行带有销售但没有费用,还有几行带有费用但没有销售,到目前为止的所有内容,但我也得到了一些同时包含销售和费用以及重复的“状态”列......没什么大问题,但感觉不对劲......
1赞 philipxy 2/7/2020
@JairoLozano 查询不需要约束。尽管当约束成立时,额外的查询会返回所需的答案,否则不会。约束不会影响给定参数返回的完全联接内容。您描述的问题是您编写的查询是错误的查询。(大概是常见的错误,人们想要一些子查询的联接,每个联接可能涉及不同的键,每个子查询都可能涉及联接和/或聚合,但他们错误地尝试先进行所有联接,然后进行所有聚合或聚合。
3赞 ysth 8/16/2020
所有做 UNION 而不是 UNION ALL 的答案都是不正确的。所有带有子查询或 3 个联合选择的答案都是低效的。正确答案将与第二个表中的选择进行所有左联接的合并,第一个表上不存在 where 不存在(或等效的外部联接 + where =NULL 条件)

答:

877赞 Pablo Santa Cruz 1/26/2011 #1

MySQL中没有完整的连接,但您可以肯定地模拟它们

对于从此 Stack Overflow 问题转录的代码示例,您有:

使用两个表 t1、t2:

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id

上述查询适用于完全外部联接操作不会生成任何重复行的特殊情况。上面的查询依赖于 set 运算符来删除查询模式引入的重复行。我们可以通过对第二个查询使用反联接模式来避免引入重复行,然后使用 UNION ALL set 运算符来组合这两个集合。在更一般的情况下,如果一个完整的外部连接会返回重复的行,我们可以这样做:UNION

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION ALL
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
WHERE t1.id IS NULL

评论

45赞 Pavle Lekic 3/20/2013
其实你写的东西是不正确的。因为当你做一个UNION时,你会删除重复项,有时当你连接两个不同的表时,应该有重复项。
6赞 The Impaler 8/24/2022
这个答案是错误的。它将删除重复的行。
2赞 Peter Mortensen 8/26/2022
@The Impaler:这里有一些矛盾。得票最高的答案以“巴勃罗·圣克鲁斯给出的答案是正确的”开头。也许更具体地说明哪些答案和哪些评论支持这一说法?
1赞 The Impaler 8/26/2022
@NickstandswithUkraine 这只会增加混乱。我删除了警告。随意添加一个下注警告,这样天真的用户就不会使用第一个解决方案。也许它应该被完全删除。
1赞 Nick is tired 8/26/2022
@TheImpaler我必须同意第二个代码块之前的解释是......写得不好,令人困惑,我只是不认为[第一个块是第一个]是必然将答案作为一个整体错误的理由。我认为更好的解决方案是重新排序它,以便第二部分排在第一位,而(当前)第一部分更像是不需要重复行的附录。但是我并不是一个真正的SQL人,所以应该把它留给那些知道他们在写什么的人。
442赞 Nathan Long 2/10/2012 #2

巴勃罗·圣克鲁斯给出的答案是正确的;但是,如果有人偶然发现此页面并希望获得更多澄清,这里有详细的细分。

示例表

假设我们有以下表格:

-- t1
id  name
1   Tim
2   Marta

-- t2
id  name
1   Tim
3   Katarina

内部连接

内部联接,如下所示:

SELECT *
FROM `t1`
INNER JOIN `t2` ON `t1`.`id` = `t2`.`id`;

只会让我们看到出现在两个表中的记录,如下所示:

1 Tim  1 Tim

内部连接没有方向(如左或右),因为它们是明确的双向的 - 我们需要两边的匹配。

外部连接

另一方面,外部联接用于查找在另一个表中可能不匹配的记录。因此,您必须指定允许联接的哪一端有缺失的记录。

LEFT JOINand 是 和 的简写;我将在下面使用它们的全名来强化外部连接与内部连接的概念。RIGHT JOINLEFT OUTER JOINRIGHT OUTER JOIN

左外连接

左外联接,如下所示:

SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;

...将我们从左表中获取所有记录,无论它们在右表中是否匹配,如下所示:

1 Tim   1    Tim
2 Marta NULL NULL

右外连接

右外连接,如下所示:

SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;

...将我们从右侧表中获取所有记录,无论它们在左侧表中是否匹配,如下所示:

1    Tim   1  Tim
NULL NULL  3  Katarina

完全外部连接

完整的外部联接将向我们提供两个表中的所有记录,无论它们在另一个表中是否匹配,两端的 NULL 都没有匹配。结果如下所示:

1    Tim   1    Tim
2    Marta NULL NULL
NULL NULL  3    Katarina

然而,正如Pablo Santa Cruz所指出的,MySQL不支持这一点。我们可以通过执行左连接和右连接的 UNION 来模拟它,如下所示:

SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`

UNION

SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;

你可以把 a 看作是“运行这两个查询,然后将结果堆叠在一起”;有些行来自第一个查询,有些来自第二个查询。UNION

应该注意的是,MySQL中的a将消除完全重复的:Tim将出现在这里的两个查询中,但结果只列出了他一次。我的数据库专家同事认为这种行为不应该被依赖。因此,为了更明确地说明这一点,我们可以在第二个查询中添加一个子句:UNIONUNIONWHERE

SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`

UNION

SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`
WHERE `t1`.`id` IS NULL;

另一方面,如果您出于某种原因查看重复项,则可以使用 .UNION ALL

评论

5赞 Garen 2/12/2014
对于MySQL,如果没有重叠,你真的希望避免使用UNION而不是UNION ALL(参见上面Pavle的评论)。如果你能在这里的回答中添加更多的信息,我认为这将是这个问题的首选答案,因为它更彻底。
3赞 spencer7593 5/7/2015
“数据库大师同事”的建议是正确的。就关系模型而言(Ted Codd 和 Chris Date 所做的所有理论工作),最后一个形式的查询模拟 FULL OUTER JOIN,因为它组合了两个不同的集合,第二个查询不会引入不会由 .以这种方式进行查询并使用 UNION 删除这些重复项并没有错。但是要真正复制一个 ,我们需要其中一个查询是反连接。FULL OUTER JOINFULL OUTER JOIN
2赞 spencer7593 11/6/2017
@IstiaqueAhmed:目标是模拟 FULL OUTER JOIN 操作。我们在第二个查询中需要该条件,因此它仅返回不匹配的行(反联接模式)。如果没有该条件,查询是外部联接...它返回匹配的行以及不匹配的行。第一个查询已经返回了匹配的行。如果第二个查询(再次)返回相同的行,则我们重复了行,并且结果将不等同于 FULL OUTER JOIN。
2赞 spencer7593 11/6/2017
@IstiaqueAhmed:确实,操作会删除这些重复项;但它也会删除所有重复的行,包括 FULL OUTER JOIN 返回的重复行。要模拟,正确的模式是 。UNIONa FULL JOIN b(a LEFT JOIN b) UNION ALL (b ANTI JOIN a)
1赞 ysth 8/16/2020
很好的解释,但是当 FULL OUTER JOIN 没有时,UNION 而不是 UNION ALL 会删除重复的行
4赞 Rami Jamleh 1/14/2013 #3

在SQLite中,您应该这样做:

SELECT * 
FROM leftTable lt 
LEFT JOIN rightTable rt ON lt.id = rt.lrid 
UNION
SELECT lt.*, rl.*  -- To match column set
FROM rightTable rt 
LEFT JOIN  leftTable lt ON lt.id = rt.lrid

评论

0赞 Kabir Hossain 10/14/2015
我们可以使用它吗?like as: SELECT * FROM leftTable lt LEFT JOIN rightTable rt ON lt.id = rt.lrid UNION SELECT lt.*, rl.* -- 匹配列集 FROM leftTable lt RIGHT JOIN rightTable rt ON lt.id = rt.lrid;
0赞 Rami Jamleh 4/20/2016
是的,但是SQLite不支持正确的连接,但是在MYSQL中是的
0赞 The Impaler 8/24/2022
这个答案是错误的。在重复行(多集)的情况下,它会产生错误的结果。
-2赞 Alex Pliutau 10/24/2013 #4
SELECT
    a.name,
    b.title
FROM
    author AS a
LEFT JOIN
    book AS b
    ON a.id = b.author_id
UNION
SELECT
    a.name,
    b.title
FROM
    author AS a
RIGHT JOIN
    book AS b
    ON a.id = b.author_id

评论

1赞 The Impaler 8/24/2022
这个答案是错误的。在重复行(多集)的情况下,它会产生错误的结果。
-6赞 alamelu 2/3/2014 #5

这也是可能的,但您必须在 select 中提及相同的字段名称。

SELECT t1.name, t2.name FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT t1.name, t2.name FROM t2
LEFT JOIN t1 ON t1.id = t2.id

评论

1赞 Matthew Read 3/27/2018
这只是复制左联接的结果。
1赞 Ankit Jindal 9/5/2020
这不会给出与完全外部连接等效的结果。
51赞 shA.t 5/28/2015 #6

使用联合查询将删除重复项,这与从不删除任何重复项的完全外部联接行为不同:

[Table: t1]        [Table: t2]
value              value
-----------        -------
1                  1
2                  2
4                  2
4                  5

这是完全外部联接的预期结果:

value | value
------+-------
1     | 1
2     | 2
2     | 2
Null  | 5
4     | Null
4     | Null

这是将 leftright joinunion 一起使用的结果:

value | value
------+-------
Null  | 5
1     | 1
2     | 2
4     | Null

SQL 小提琴

我建议的查询是:

select
    t1.value, t2.value
from t1
left outer join t2
  on t1.value = t2.value
union all      -- Using `union all` instead of `union`
select
    t1.value, t2.value
from t2
left outer join t1
  on t1.value = t2.value
where
    t1.value IS NULL

上述查询的结果与预期结果相同:

value | value
------+-------
1     | 1
2     | 2
2     | 2
4     | NULL
4     | NULL
NULL  | 5

SQL 小提琴


@Steve 钱伯斯[来自评论,非常感谢!

注意:这可能是最好的解决方案,无论是提高效率还是生成与 .这篇博文也很好地解释了这一点 - 引用方法 2:“这正确地处理了重复的行,并且不包含任何不应该包含的内容。有必要使用 UNION ALL 而不是普通的 UNION,这将消除我想保留的重复项。这在大型结果集上可能效率更高,因为无需对重复项进行排序和删除。FULL OUTER JOIN


我决定添加另一个解决方案,该解决方案来自完整的外部连接可视化和数学运算。它并不比上面更好,但它更具可读性:

完全外部连接意味着:全部在或在:所有在两者中,加上所有不在中和加所有在不在:(t1 ∪ t2)t1t2(t1 ∪ t2) = (t1 ∩ t2) + t1_only + t2_onlyt1t2t1t2t2t1

-- (t1 ∩ t2): all in both t1 and t2
select t1.value, t2.value
from t1 join t2 on t1.value = t2.value
union all  -- And plus
-- all in t1 that not exists in t2
select t1.value, null
from t1
where not exists( select 1 from t2 where t2.value = t1.value)
union all  -- and plus
-- all in t2 that not exists in t1
select null, t2.value
from t2
where not exists( select 1 from t1 where t2.value = t1.value)

SQL 小提琴

评论

7赞 Steve Chambers 7/22/2016
这种方法似乎是最好的解决方案,无论是提高效率还是生成与 .这篇博文也很好地解释了这一点 - 引用方法 2:“这正确地处理了重复的行,并且不包含任何不应该包含的内容。有必要使用 UNION ALL 而不是普通的 UNION,这将消除我想保留的重复项。这在大型结果集上可能效率更高,因为无需对重复项进行排序和删除。FULL OUTER JOIN
0赞 Aaron D. Marasco 5/31/2023
可悲的是,如果您尝试使用临时表,这在MySQL或旧版本的MariaDB中都不起作用。自 2005 年以来的 MySQL 错误MariaDB 已在 10.2.1 中修复
5赞 a20 3/11/2016 #7

为了更清楚起见,我修改了 shA.t 的查询

-- t1 left join t2
SELECT t1.value, t2.value
FROM t1 LEFT JOIN t2 ON t1.value = t2.value   

    UNION ALL -- include duplicates

-- t1 right exclude join t2 (records found only in t2)
SELECT t1.value, t2.value
FROM t1 RIGHT JOIN t2 ON t1.value = t2.value
WHERE t1.value IS NULL 
13赞 Gordon Linoff 1/14/2017 #8

前面的答案实际上都不正确,因为当存在重复值时,它们不遵循语义。

对于诸如(来自此副本)的查询:

SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.Name = t2.Name;

正确的等价物是:

SELECT t1.*, t2.*
FROM (SELECT name FROM t1 UNION  -- This is intentionally UNION to remove duplicates
      SELECT name FROM t2
     ) n LEFT JOIN
     t1
     ON t1.name = n.name LEFT JOIN
     t2
     ON t2.name = n.name;

如果需要它来处理值(也可能是必要的),请使用 -safe 比较运算符,而不是 .NULLNULL<=>=

-4赞 Angelos 2/9/2017 #9

用:

SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.id = t2.id;

可以按如下方式重新创建它:

 SELECT t1.*, t2.*
 FROM (SELECT * FROM t1 UNION SELECT name FROM t2) tmp
 LEFT JOIN t1 ON t1.id = tmp.id
 LEFT JOIN t2 ON t2.id = tmp.id;

使用 UNION 或 UNION ALL 答案不包括基表具有重复条目的边缘情况。

解释:

存在 UNION 或 UNION ALL 无法涵盖的边缘情况。我们无法在 MySQL 上测试这一点,因为它不支持完整的外部连接,但我们可以在支持它的数据库上说明这一点:

 WITH cte_t1 AS
 (
     SELECT 1 AS id1
     UNION ALL SELECT 2
     UNION ALL SELECT 5
     UNION ALL SELECT 6
     UNION ALL SELECT 6
 ),
cte_t2 AS
(
     SELECT 3 AS id2
     UNION ALL SELECT 4
     UNION ALL SELECT 5
     UNION ALL SELECT 6
     UNION ALL SELECT 6
)
SELECT  *  FROM  cte_t1 t1 FULL OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2;

这给了我们这个答案:

id1  id2
1  NULL
2  NULL
NULL  3
NULL  4
5  5
6  6
6  6
6  6
6  6

UNION解决方案:

SELECT  * FROM  cte_t1 t1 LEFT OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2
UNION    
SELECT  * FROM cte_t1 t1 RIGHT OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2

给出错误的答案:

 id1  id2
NULL  3
NULL  4
1  NULL
2  NULL
5  5
6  6

UNION ALL解决方案:

SELECT  * FROM cte_t1 t1 LEFT OUTER join cte_t2 t2 ON t1.id1 = t2.id2
UNION ALL
SELECT  * FROM  cte_t1 t1 RIGHT OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2

也是不正确的。

id1  id2
1  NULL
2  NULL
5  5
6  6
6  6
6  6
6  6
NULL  3
NULL  4
5  5
6  6
6  6
6  6
6  6

而这个查询:

SELECT t1.*, t2.*
FROM (SELECT * FROM t1 UNION SELECT name FROM t2) tmp
LEFT JOIN t1 ON t1.id = tmp.id
LEFT JOIN t2 ON t2.id = tmp.id;

给出以下内容:

id1  id2
1  NULL
2  NULL
NULL  3
NULL  4
5  5
6  6
6  6
6  6
6  6

顺序不同,但在其他方面与正确答案匹配。

评论

0赞 Marc L. 7/25/2017
这很可爱,但歪曲了解决方案。此外,它还提供了一种解决方案,由于需要重复数据消除,因此在大型源表上使用该解决方案的速度会较慢。最后,它不会编译,因为该字段在子查询中不存在。UNION ALLUNIONidtmp
0赞 Angelos 7/25/2017
我从来没有对速度提出过要求,OP也没有提到任何关于速度的事情。假设 UNION ALL(你不依赖于指定哪一个)并且这都给出了正确的答案,如果我们想断言一个更快,我们需要提供基准,这将偏离 OP 问题。
0赞 Angelos 7/25/2017
至于关于id不在子查询中的观察结果,我纠正了错别字 - 谢谢你指出来。你的虚假陈述是模糊的 - 如果你能提供更多信息,我可以解决这个问题。关于你对可爱的最后观察,我没有任何评论,我宁愿专注于sql的逻辑。
4赞 Marc L. 7/26/2017
歪曲:“解决方案:......也是不正确的。您提供的代码省略了必须在 中提供的 right-join () 中的 intersection-exclusion。也就是说,只有当其他解决方案之一实施不正确时,您的解决方案才会胜过所有其他解决方案。关于“可爱”,观点。这是无缘无故的,我很抱歉。UNION ALLwhere t1.id1 is nullUNION ALL
1赞 philipxy 8/26/2022
表没有行顺序,结果集有按顺序排列的行部分顺序,但你没有使用顺序依据,所以你碰巧得到行的顺序不是语言指定结果定义的一部分,所以提到“顺序不同,但在其他方面与正确答案匹配”是不清楚的,误解和误导的。“使用 UNION 或 UNION ALL 答案不包括”——当然可以编写一个查询,返回与这些答案的完全连接相同,所以这是错误的。也许你想说的话是对的,但如果是这样,你就没有说出来。
-3赞 Rubén Ruíz 5/23/2017 #10

我修复了响应,并且作品包括所有行(基于 Pavle Lekic 的响应):

    (
    SELECT a.* FROM tablea a
    LEFT JOIN tableb b ON a.`key` = b.key
    WHERE b.`key` is null
    )
    UNION ALL
    (
    SELECT a.* FROM tablea a
    LEFT JOIN tableb b ON a.`key` = b.key
    where  a.`key` = b.`key`
    )
    UNION ALL
    (
    SELECT b.* FROM tablea a
    right JOIN tableb b ON b.`key` = a.key
    WHERE a.`key` is null
    );

评论

1赞 Marc L. 7/25/2017
不,这是一种“仅外部”联接,它只会返回没有匹配项的行,反之亦然。u 尝试 ,这只有在这两个表具有等效排序的列时才有效,这并不能保证。tableatablebUNION ALL
0赞 Rubén Ruíz 7/28/2017
它有效,我在临时数据库 tablea(1,2,3,4,5,6) 和 tableb(4,5,6,7,8,9) 上创建,它的行有 3 列“id”、“number”和“name_number”作为文本,而结果中只有 (1,2,3,7,8,9)
2赞 Marc L. 7/28/2017
这不是外部连接。外部联接还包括匹配的成员。
0赞 Rubén Ruíz 8/2/2017
那个新句子的所有结果都是 1,2, ..., 9
-4赞 Super Mario 6/14/2017 #11

使用交叉联接解决方案:

SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2 
ON 1=1;

评论

4赞 Marc L. 7/25/2017
不,这是交叉联接。它将 t1 中的每一行与 t2 中的每一行进行匹配,从而生成所有可能组合的集合,并在结果集中包含行。select (select count(*) from t1) * (select count(*) from t2))
1赞 Alexander 3/27/2018
虽然此代码可以回答问题,但提供有关它如何以及为何解决问题的其他上下文将提高答案的长期价值。
1赞 Super Mario 3/29/2018
哪些添加可能会有所帮助?也许举个例子?
11赞 ARK 10/13/2017 #12

MySQL没有FULL-OUTER-JOIN语法。您必须通过执行 LEFT JOIN 和 RIGHT JOIN 来模拟它,如下所示:

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id

但是MySQL也没有RIGHT JOIN语法。根据 MySQL 的外部连接简化,通过切换查询中 and 子句中的 t1 和 t2,将右连接转换为等效的左连接。因此,MySQL查询优化器将原始查询转换为以下内容 -FROMON

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t2
LEFT JOIN t1 ON t2.id = t1.id

现在,按原样编写原始查询并没有什么坏处,但是假设如果您有像 WHERE 子句这样的谓词,它是 before-join 谓词,或者是子句上的 AND 谓词,这是一个 during-join 谓词,那么你可能想看看魔鬼;这是细节。ON

MySQL查询优化器会定期检查谓词是否被拒绝。

Null-Rejected Definition and Examples

现在,如果您已经完成了 RIGHT JOIN,但对 t1 中的列使用了 WHERE 谓词,则可能会遇到 null 拒绝的情况。

例如,查询

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
WHERE t1.col1 = 'someValue'
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
WHERE t1.col1 = 'someValue'

由查询优化器转换为以下内容:

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
WHERE t1.col1 = 'someValue'
UNION
SELECT * FROM t2
LEFT JOIN t1 ON t2.id = t1.id
WHERE t1.col1 = 'someValue'

因此,表的顺序已更改,但谓词仍应用于 t1,但 t1 现在位于“ON”子句中。如果将 t1.col1 定义为列,则此查询将被拒绝为 nullNOT NULL

任何被 null 拒绝的外部连接(左、右、全)都会被 MySQL 转换为内部连接。

因此,您可能期望的结果可能与MySQL返回的结果完全不同。您可能认为这是MySQL的RIGHT JOIN的错误,但这是不对的。这就是MySQL查询优化器的工作方式。因此,负责的开发人员在构建查询时必须注意这些细微差别。

评论

1赞 philipxy 8/26/2022
“但是MySQL也没有RIGHT JOIN语法。是错误的。此外,DBMS在计算/实现/优化语言指定结果方面所做的工作与问题无关。
3赞 lolo 6/5/2018 #13

您可以执行以下操作:

(SELECT 
    *
FROM
    table1 t1
        LEFT JOIN
    table2 t2 ON t1.id = t2.id
WHERE
    t2.id IS NULL)
UNION ALL
 (SELECT 
    *
FROM
    table1 t1
        RIGHT JOIN
    table2 t2 ON t1.id = t2.id
WHERE
    t1.id IS NULL);
-9赞 philipxy 8/12/2018 #14

SQL标准说是行不匹配,左边表行由空值扩展,右表行由空值扩展。即行在但不是行中但不是行。full join oninner join onunion allunion allinner join onunion allleft join oninner join onunion allright join oninner join on

即行不在 .或者,如果您知道您的结果在特定的右表列中不能有 null,那么“不在”中的行是具有该列扩展条件的行。left join onunion allright join oninner join oninner join onright join oninner join onright join ononandis null

即同样合适的行。right join onunion allleft join on

“INNER JOIN”和“OUTER JOIN”有什么区别?

(SQL Standard 2006 SQL/Foundation 7.7 语法规则 1、一般规则 1 b、3 c 和 d、5 b.)

1赞 ysth 8/18/2020 #15

您可以只转换一个完整的外部连接,例如

SELECT fields
FROM firsttable
FULL OUTER JOIN secondtable ON joincondition

到:

SELECT fields
FROM firsttable
LEFT JOIN secondtable ON joincondition
UNION ALL
SELECT fields (replacing any fields from firsttable with NULL)
FROM secondtable
WHERE NOT EXISTS (SELECT 1 FROM firsttable WHERE joincondition)

或者,如果您至少有一列,比如 ,其中不是 NULL,您可以执行以下操作:foofirsttable

SELECT fields
FROM firsttable
LEFT JOIN secondtable ON joincondition
UNION ALL
SELECT fields
FROM firsttable
RIGHT JOIN secondtable ON joincondition
WHERE firsttable.foo IS NULL