如何在MySQL中进行完整的外部连接?
我想在MySQL中做一个完整的外部连接。这可能吗?MySQL支持完全外部联接吗?您在MySQL上没有完全联接,但可以确定 对于从中转录的代码示例,您有: 有两个表t1、t2:如何在MySQL中进行完整的外部连接?,sql,mysql,join,outer-join,full-outer-join,Sql,Mysql,Join,Outer Join,Full Outer Join,我想在MySQL中做一个完整的外部连接。这可能吗?MySQL支持完全外部联接吗?您在MySQL上没有完全联接,但可以确定 对于从中转录的代码示例,您有: 有两个表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 上述查询适用于完全外部联接操作不会产生任何重复行的特殊情况。上面的查询依赖于UNIONset操作符来删除查询模式引入的
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
上述查询适用于完全外部联接操作不会产生任何重复行的特殊情况。上面的查询依赖于
UNION
set操作符来删除查询模式引入的重复行。通过对第二个查询使用反连接模式,然后使用UNION ALL set运算符组合这两个集合,可以避免引入重复行。在更一般的情况下,如果完全外部联接将返回重复的行,我们可以这样做:
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
给出的答案是正确的;然而,如果有人无意中发现了这一页,并希望得到更多的澄清,这里有一个详细的分类
示例表
假设我们有以下表格:
-- 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
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
1 Tim 1 Tim
2 Marta NULL NULL
NULL NULL 3 Katarina
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`;
将仅获取两个表中出现的记录,如下所示:
SELECT *
FROM `t1`
INNER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
1 Tim 1 Tim
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
1 Tim 1 Tim
2 Marta NULL NULL
NULL NULL 3 Katarina
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`;
内部连接没有方向(如左或右),因为它们是显式双向的-我们需要在两侧进行匹配
外连接
另一方面,外部联接用于查找在另一个表中可能不匹配的记录。因此,您必须指定允许联接的哪一侧有丢失的记录
左连接
和右连接
是左外连接
和右外连接
的缩写;下面我将使用它们的全名来强化外部联接与内部联接的概念
左外连接
左外连接,如下所示:
SELECT *
FROM `t1`
INNER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
1 Tim 1 Tim
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
1 Tim 1 Tim
2 Marta NULL NULL
NULL NULL 3 Katarina
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`;
…将从左表中获取所有记录,无论右表中是否有匹配项,如下所示:
SELECT *
FROM `t1`
INNER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
1 Tim 1 Tim
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
1 Tim 1 Tim
2 Marta NULL NULL
NULL NULL 3 Katarina
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`;
右外连接
右外连接,如下所示:
SELECT *
FROM `t1`
INNER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
1 Tim 1 Tim
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
1 Tim 1 Tim
2 Marta NULL NULL
NULL NULL 3 Katarina
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`;
…将从右表中获取所有记录,无论左表中是否有匹配项,如下所示:
SELECT *
FROM `t1`
INNER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
1 Tim 1 Tim
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
1 Tim 1 Tim
2 Marta NULL NULL
NULL NULL 3 Katarina
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`;
完全外接
一个完整的外部联接将为我们提供两个表中的所有记录,无论它们在另一个表中是否匹配,在不匹配的两侧都为null。结果如下所示:
SELECT *
FROM `t1`
INNER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
1 Tim 1 Tim
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
1 Tim 1 Tim
2 Marta NULL NULL
NULL NULL 3 Katarina
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`;
然而,正如PabloSanta Cruz所指出的,MySQL不支持这一点。我们可以通过左连接和右连接的并集来模拟它,如下所示:
SELECT *
FROM `t1`
INNER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
1 Tim 1 Tim
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
1 Tim 1 Tim
2 Marta NULL NULL
NULL NULL 3 Katarina
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;
另一方面,如果出于某种原因希望看到重复项,可以在SQLite中使用
UNION ALL
,您应该这样做:
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中提及相同的字段名
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
使用
联合
查询将删除重复项,这与从不删除任何重复项的完全外部联接
的行为不同:
[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
这是将左
和右联接
与联合
一起使用的结果:
value | value
------+-------
Null | 5
1 | 1
2 | 2
4 | Null
我的建议是:
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
:[来自评论,非常感谢!]
注意:从效率和生成与
完全外部联接相同的结果两方面考虑,这可能是最好的解决方案。也很好地解释了这一点——引用方法2:“这将正确处理重复的行,并且不包含任何不应该包含的内容。有必要使用UNION ALL
而不是普通的UNION
,这将消除我想要保留的重复项。这在大型结果集上可能更有效,因为不需要排序和删除重复项。”
我决定添加另一个解决方案,该解决方案来自完全外部连接
可视化和数学,这并不是更好,而是更具可读性:
完全外部连接意味着(t1∪ t2)
:全部在t1
或t2
(t1∪ t2)=(t1∩ t2)+t1_only+t2_only
:全部在t1
和t2
中加上全部在t1
中不在t2
中加上全部在t2
中不在t1
中:
为了更加清晰,修改了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
上述答案实际上都不正确,因为它们在存在重复值时不遵循语义
对于查询,例如(来自此):
正确的等价物是:
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
-安全比较运算符
,而不是=回答:
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答案不包括基表具有重复项的边缘情况
说明:
SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.id = t2.id;
有一种边缘情况,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
欧盟解决方案:
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
欧盟所有解决方案:
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
(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);
顺序不同,但在其他方面与正确答案相匹配。我修复了响应,并且