当前位置: 代码迷 >> 综合 >> 如何在MySQL中进行FULL OUTER JOIN?
  详细解决方案

如何在MySQL中进行FULL OUTER JOIN?

热度:60   发布时间:2024-01-24 13:56:35.0

我想在MySQL中进行完全外部联接。 这可能吗? MySQL是否支持完全外部联接?


#1楼

在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

#2楼

SELECTa.name,b.title
FROMauthor AS a
LEFT JOINbook AS bON a.id = b.author_id
UNION
SELECTa.name,b.title
FROMauthor AS a
RIGHT JOINbook AS bON a.id = b.author_id

#3楼

也可以,但必须在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

#4楼

使用union查询将删除重复项,这与从不删除任何重复项的full outer join的行为不同:

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

这是full outer join的预期结果:

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 : [评论,非常感谢!]
注意:这可能是最好的解决方案,从效率和产生与FULL OUTER JOIN相同的结果FULL OUTER JOIN 。 这篇博客文章也很好地解释了这一点-引用方法2: “此方法可以正确处理重复的行,并且不包含不应包含的任何内容。有必要使用UNION ALL而不是普通的UNION ,这样可以消除我想要的重复项保持。对于大型结果集,这可能会显着提高效率,因为无需排序和删除重复项。”


我决定添加另一种解决方案,该解决方案来自full outer join可视化和数学计算,这不是上面的更好,但更具可读性:

完全外部联接方式(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]


#5楼

修改了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 t2.value IS NULL 

#6楼

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

对于诸如(来自此重复项 )的查询:

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 duplicatesSELECT name FROM t2) n LEFT JOINt1ON t1.name = n.name LEFT JOINt2ON t2.name = n.name;

如果您需要使用NULL值(这可能也是必要的),请使用NULL -safe比较运算符<=>而不是= 。


#7楼

回答:

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

可以重新创建如下:

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

使用UNION或UNION ALL答案不能解决基本表具有重复条目的情况。

说明:

在某些情况下,UNION或UNION ALL无法覆盖。 我们不能在mysql上测试它,因为它不支持FULL OUTER JOINs,但是我们可以在支持它的数据库上对此进行说明:

 WITH cte_t1 AS(SELECT 1 AS id1UNION ALL SELECT 2UNION ALL SELECT 5UNION ALL SELECT 6UNION ALL SELECT 6),
cte_t2 AS
(SELECT 3 AS id2UNION ALL SELECT 4UNION ALL SELECT 5UNION ALL SELECT 6UNION 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

顺序不同,但是与正确答案匹配。


#8楼

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

    (SELECT a.* FROM tablea aLEFT JOIN tableb b ON a.`key` = b.keyWHERE b.`key` is null)UNION ALL(SELECT a.* FROM tablea aLEFT JOIN tableb b ON a.`key` = b.keywhere  a.`key` = b.`key`)UNION ALL(SELECT b.* FROM tablea aright JOIN tableb b ON b.`key` = a.keyWHERE a.`key` is null);

#9楼

您对交叉联接解决方案怎么说?

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

#10楼

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的外部 FROM 简化 ,通过在查询的FROM和ON子句中切换t1和t2,将右FROM转换为等效的左FROM 。 因此,MySql Query Optimizer会将原始查询转换为以下内容-

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

现在,按原样编写原始查询没有什么害处,但是如果您有谓词,例如WHERE子句(是连接前的谓词)或ON子句的AND谓词(是连接中的谓词),则您可能想看看魔鬼; 这是详细信息。

MySql查询优化器会定期检查谓词是否被null拒绝 。 现在,如果您已完成RIGHT JOIN,但在t1的列上使用了WHERE谓词,则可能有陷入空值拒绝情况的风险。

例如,以下查询-

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'

由Query Optimizer转换为以下内容-

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 。

MySql将任何被null拒绝的外部联接(左,右,完整)转换为内部联接。

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


#11楼

您在MySQL上没有FULL JOINS,但是可以肯定地模拟它们 。

对于从该SO问题记录下来的代码SAMPLE,您可以:

有两个表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

上面的查询适用于FULL OUTER JOIN操作不会产生任何重复行的特殊情况。 上面的查询取决于UNION集运算符,以删除查询模式引入的重复行。 我们可以通过对第二个查询使用反联接模式来避免引入重复的行,然后使用UNION ALL集运算符将这两个集合并。 在更一般的情况下,FULL OUTER JOIN将返回重复的行,我们可以这样做:

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

#12楼

您可以执行以下操作:

(SELECT *
FROMtable1 t1LEFT JOINtable2 t2 ON t1.id = t2.id
WHEREt2.id IS NULL)
UNION ALL(SELECT *
FROMtable1 t1RIGHT JOINtable2 t2 ON t1.id = t2.id
WHEREt1.id IS NULL);

#13楼

SQL标准说full join on是行full join on的inner join on union all不匹配的左表行扩展为null,将union all右表行扩展为null。 即行inner join on合并left join on union all行,但不inner join on right join on union all行,但不inner join on 。

即left join on行union all行, right join on行union all行不在inner 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在行right join on联接。

“内联接”和“外联接”有什么区别? :

(SQL Standard 2006 SQL / Foundation 7.7语法规则1,通用规则1b,3c和d,5b。)


#14楼

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`UNIONSELECT *
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`UNIONSELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`
WHERE `t1`.`id` IS NULL;

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

  相关解决方案