如何在 MySQL 中进行 FULL OUTER JOIN?
Posted
技术标签:
【中文标题】如何在 MySQL 中进行 FULL OUTER JOIN?【英文标题】:How can I do a FULL OUTER JOIN in MySQL? 【发布时间】:2021-09-06 07:07:05 【问题描述】:我想在 mysql 中做一个 full outer join。这可能吗? MySQL 是否支持全外连接?
【问题讨论】:
MySQL Full Outer Join Syntax Error的可能重复 这个问题有更好的答案 当心这里的答案。 SQL 标准说完全连接是对行的内连接联合所有不匹配的左表行由空值扩展联合所有右表行由空值扩展。这里的大多数答案都是错误的(参见 cmets)& 没有错误的答案不能处理一般情况。即使有很多(不合理的)赞成票。 (见我的回答。) @JairoLozano 查询不需要约束。尽管当约束持有额外的查询时会返回所需的答案,否则不会。约束不影响给定参数返回的完全连接。您描述的问题是您编写的查询是错误的查询。 (大概是人们想要一些连接的常见错误,每个连接都可能涉及不同的键,一些子查询,每个可能涉及连接和/或聚合,但他们错误地尝试先进行所有连接,然后再进行所有聚合或聚合以前的聚合.) 所有使用 UNION 而不是 UNION ALL 的答案都不正确。所有带有子查询或 3 个联合选择的答案都是低效的。正确的答案将联合所有的左连接与从第二个表中选择的一个在第一个表上不存在的地方(或等效的外连接 + where =NULL 条件) 【参考方案1】:您在 MySQL 中没有完全连接,但您可以确定 emulate them。
对于从this Stack Overflow question 转录的代码示例,您有:
有两个表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
上面的查询适用于 完全外连接 操作不会产生任何重复行的特殊情况。上面的查询依赖于UNION
集合运算符来删除查询模式引入的重复行。我们可以通过对第二个查询使用 anti-join 模式来避免引入重复行,然后使用 UNION ALL 集合运算符来组合这两个集合。在更一般的情况下,完全外连接会返回重复的行,我们可以这样做:
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
【讨论】:
其实你写的不正确。因为当您执行 UNION 时,您将删除重复项,有时当您连接两个不同的表时,应该会有重复项。 这是正确的例子:(SELECT ... FROM tbl1 LEFT JOIN tbl2 ...) UNION ALL (SELECT ... FROM tbl1 RIGHT JOIN tbl2 ... WHERE tbl1.col IS NULL)
所以不同之处在于我正在使用 UNION ALL 进行左包含连接,然后右排
我现在看到你自己这么说,对不起。也许您可以更新您的答案,因为在这种情况下它会出错并且 UNION ALL 总是会更有效率?
@ypercube:如果t1
和t2
中没有重复行,则此答案中的查询会返回一个模拟完整外部联接的结果集。但在更一般的情况下,例如,SELECT 列表不包含足够的列/表达式来使返回的行唯一,那么这个查询模式 insufficient 来重现将由一个FULL OUTER JOIN
。为了获得更忠实的模拟,我们需要一个 UNION ALL
集合运算符,其中一个查询需要一个 anti-join 模式。 Pavle Lekic(上图)的评论给出了正确的查询模式。【参考方案2】:
Pablo Santa Cruz给出的答案是正确的;但是,如果有人偶然发现此页面并希望了解更多信息,请参阅此处的详细分类。
示例表
假设我们有以下表格:
-- 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 JOIN
和 RIGHT JOIN
是 LEFT OUTER JOIN
和 RIGHT 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`;
您可以将UNION
视为“运行这两个查询,然后将结果堆叠在一起”;一些行将来自第一个查询,一些来自第二个查询。
需要注意的是,MySQL 中的UNION
将消除精确重复:Tim 会出现在此处的两个查询中,但UNION
的结果只列出了他一次。我的数据库专家同事认为不应依赖这种行为。所以为了更明确一点,我们可以在第二个查询中添加一个WHERE
子句:
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
。
【讨论】:
对于 MySQL,如果没有重叠,您真的希望避免使用 UNION 而不是 UNION ALL(请参阅上面 Pavle 的评论)。如果您可以在此处的答案中添加更多信息,我认为这将是这个问题的首选答案,因为它更彻底。 “数据库大师同事”的建议是正确的。就关系模型(Ted Codd 和 Chris Date 完成的所有理论工作)而言,最后一种形式的查询模拟了 FULL OUTER JOIN,因为它结合了两个不同的集合,第二个查询不引入“重复”(第一个查询已经返回的行)不会由FULL OUTER JOIN
生成。以这种方式进行查询并使用 UNION 删除这些重复项并没有错。但要真正复制FULL OUTER JOIN
,我们需要其中一个查询是反连接。
@IstiaqueAhmed:目标是模拟 FULL OUTER JOIN 操作。我们在第二个查询中需要该条件,因此它只返回不匹配的行(反连接模式。)。没有那个条件,查询就是一个外连接......它返回匹配的行以及不匹配的行。匹配的行已经由第一个查询返回。如果第二个查询(再次)返回相同的行,我们已经复制了行,我们的结果将不等同于 FULL OUTER JOIN。
@IstiaqueAhmed:UNION
操作确实会删除这些重复项;但它也会删除所有重复的行,包括将由 FULL OUTER JOIN 返回的重复行。要模拟a FULL JOIN b
,正确的模式是(a LEFT JOIN b) UNION ALL (b ANTI JOIN a)
。
非常简洁的答案和很好的解释。谢谢你。【参考方案3】:
使用 union 查询将删除重复项,这与永远不会删除任何重复项的 full outer join 的行为不同:
[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
这是使用 left 和 right join 与 union 的结果:
value | value
------+-------
Null | 5
1 | 1
2 | 2
4 | Null
SQL Fiddle
我建议的查询是:
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 Fiddle
@Steve Chambers: [来自 cmets,非常感谢!]
注意:这可能是最好的解决方案,无论是提高效率还是生成与FULL OUTER JOIN
相同的结果。 This blog post 也很好地解释了它 - 引用方法 2:“这可以正确处理重复的行并且不包含任何不应该包含的内容。有必要使用 UNION ALL
而不是普通的 UNION
,这将消除我想要保留的重复项。这在大型结果集上可能会显着提高效率,因为不需要排序和删除重复项。"
我决定添加另一个来自完全外连接可视化和数学的解决方案。并不比上面的好,但是可读性更强:
完全外连接意味着
(t1 ∪ t2)
:全部在t1
或t2
(t1 ∪ t2) = (t1 ∩ t2) + t1_only + t2_only
:t1
和t2
中的所有内容,加上t1
中不在t2
中的所有内容,以及t2
中不在t1
中的所有内容:
-- (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 Fiddle
【讨论】:
我们正在执行相同的任务两次,如果有 t1 和 t2 的子查询,那么 mysql 必须多次执行相同的任务,不是吗?在这种情况下我们可以使用别名删除它吗?: 我建议你使用一些临时表;)。 这种方法似乎是最好的解决方案,无论是提高效率还是生成与FULL OUTER JOIN
相同的结果。 This blog post 也很好地解释了它 - 引用方法 2:“这可以正确处理重复的行并且不包含任何不应该包含的内容。有必要使用 UNION ALL 而不是普通的 UNION,这将消除重复我想保留。这在大型结果集上可能会更有效,因为不需要排序和删除重复项。"
@SteveChambers 为时已晚,但感谢您的评论。我添加了您的评论,然后回答突出显示的更多内容,如果您不同意,请回滚;)。
没问题@shA.t - IMO 这真的应该有更多的赞成票和/或被接受的答案。【参考方案4】:
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的outer join simplification,通过在查询中切换FROM
和ON
子句中的t1和t2,将右连接转换为等效的左连接。因此,MySQL 查询优化器将原始查询转换为以下 -
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 谓词,或者是 ON
子句上的一个 AND 谓词,这是一个 @ 987654323@谓词,那你可能想看看魔鬼;这是详细的。
MySQL 查询优化器会定期检查谓词是否被 null-rejected。
现在,如果您已完成 RIGHT JOIN,但在 t1 的列上使用 WHERE 谓词,那么您可能会遇到 null-rejected 场景。
例如查询
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 定义为NOT NULL
列,那么这个查询将被null-rejected。
任何 null-rejected 的外连接(左、右、全)都会被 MySQL 转换为内连接。
因此,您可能期望的结果可能与 MySQL 返回的结果完全不同。你可能认为它是 MySQL 的 RIGHT JOIN 的一个错误,但这是不对的。它就是 MySQL 查询优化器的工作方式。因此,负责的开发人员在构建查询时必须注意这些细微差别。
【讨论】:
【参考方案5】:前面的答案都不是真正正确的,因为当存在重复值时它们不遵循语义。
对于诸如(来自this duplicate)的查询:
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;
如果您需要它来处理 NULL
值(这也可能是必需的),请使用 NULL
-安全比较运算符 <=>
而不是 =
。
【讨论】:
这通常是一个很好的解决方案,但是当name
为空时,它可能会给出与FULL OUTER JOIN
不同的结果。带有反连接模式的union all
查询应该正确地重现外连接行为,但哪种解决方案更合适取决于上下文和表上的活动约束。
@fthiella 。 . .这是一个好点。我调整了答案。
好的,但是 null 安全的比较运算符将使联接成功,这与完整的外部联接行为不同,以防您在 t1 和 t2 中都有空名称
@fthiella 。 . .我将不得不考虑最好的方法来做到这一点。但是考虑到公认的答案有多么错误,几乎任何事情都更接近正确答案。 (如果两边都有多个键,那么这个答案就是错误的。)
是的,接受的答案是错误的,作为一般解决方案,我认为使用union all
是正确的,但该答案在第一个或第二个查询中错过了将保持存在的反连接模式重复但阻止添加新的。根据上下文,其他解决方案(例如这个)可能更合适。【参考方案6】:
在 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
【讨论】:
可以用吗?如: 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 ; 是的,但 SQLite 不支持右连接,但在 MYSQL 中是的【参考方案7】:您可以执行以下操作:
(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);
【讨论】:
解释一下。请通过editing (changing) your answer 回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 答案应该看起来像是今天写的)。【参考方案8】:为了更清楚,我修改了shA.t's query:
-- 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
【讨论】:
【参考方案9】: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
【讨论】:
【参考方案10】:你可以只转换一个完整的外连接,例如
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)
或者,如果你有至少一列,比如foo
,在firsttable
中不为空,你可以这样做:
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
【讨论】:
【参考方案11】:我修复了响应,并且工作包括所有行(基于 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
);
【讨论】:
不,这是一种“仅外部”连接,它只会返回来自tablea
且在 tableb
中没有匹配的行,反之亦然。您尝试UNION ALL
,这仅在这两个表具有等价列的情况下才有效,这不能保证。
它有效,我在临时数据库 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)
这不是外连接。外连接还包括匹配的成员。
那个新句子的结果都是 1,2, ..., 9
这几乎无法理解(“我修复了响应,并且作品包括所有行”)。你能fix it吗? (但没有“编辑:”、“更新:”或类似的 - 答案应该看起来好像是今天写的。)【参考方案12】:
用途:
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;
This gives us this answer:
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
顺序不同,但其他匹配正确答案。
【讨论】:
这很可爱,但歪曲了UNION ALL
解决方案。此外,它还提供了一个使用 UNION
的解决方案,由于需要重复数据删除,这在大型源表上会更慢。最后,它不会编译,因为id
字段在子查询tmp
中不存在。
我从来没有对速度发表过声明,OP 也没有提到任何关于速度的内容。假设 UNION ALL (你不依赖指定哪一个)并且这两个都给出了正确的答案,如果我们想断言一个更快,我们需要提供基准,这将偏离 OP问题。
关于 id 不在子查询中的观察,我更正了错字 - 感谢您指出。您的虚假陈述声明含糊不清 - 如果您可以提供更多信息,我可以解决这个问题。关于你最后对可爱的观察,我没有任何意见,我宁愿关注sql的逻辑。
歪曲:“UNION ALL
解决方案:......也是不正确的。”您提供的代码省略了右连接 (where t1.id1 is null
) 中必须在 UNION ALL
中提供的交集排除。也就是说,您的解决方案胜过所有其他解决方案,只有当其中一个解决方案实施不正确时。关于“可爱”的观点。那是免费的,我很抱歉。【参考方案13】:
使用cross join 解决方案:
SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2
ON 1=1;
【讨论】:
不,这是一个交叉连接。它将 t1 中的每一行与 t2 中的每一行进行匹配,产生所有可能组合的集合,结果集中有select (select count(*) from t1) * (select count(*) from t2))
行。
虽然此代码可以回答问题,但提供有关 如何 和 为什么 解决问题的附加上下文将提高答案的长期价值。
哪些补充可能有帮助?也许是例子?【参考方案14】:
也可以,但必须在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
【讨论】:
这只是复制左连接的结果。 这不会给出与完全外连接等效的结果。【参考方案15】:SQL 标准说full join on
是inner join on
行union all
由空值扩展的不匹配左表行union all
由空值扩展的右表行。即inner join on
行union all
行在left join on
但不是inner join on
union all
行在right join on
但不是inner join on
。
即left join on
行union all
right join on
行不在inner join on
中。或者,如果您知道您的 inner join on
结果在特定的右表列中不能为空,那么“right join on
不在inner join on
中的行”是right join on
中的行,on
条件由and
扩展专栏is null
。
即同样right join on
union all
适当left join on
行。
来自What is the difference between “INNER JOIN” and “OUTER JOIN”?:
(SQL Standard 2006 SQL/Foundation 7.7 语法规则 1,一般规则 1 b、3 c & d、5 b。)
【讨论】:
这似乎有些难以理解(例如,“完全连接是对行的内连接联合所有不匹配的左表行由空值扩展联合所有右表行由空值扩展。”我>)。能改一下吗? @PeterMortensen 我会尽快编辑这篇文章。 FULL JOIN ON 返回 UNION ALL UNION ALL以上是关于如何在 MySQL 中进行 FULL OUTER JOIN?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 MySQL 中进行 FULL OUTER JOIN?
如何在 MySQL 中 FULL OUTER JOIN 多个表
MySQL:FULL OUTER JOIN - 如何合并一列?