如何在MySQL中进行FULL OUTER JOIN?

我想在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楼

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

#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

这是使用的结果, leftright Joinunion

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) :全部在t1t2
(t1 ∪ t2) = (t1 ∩ t2) + t1_only + t2_onlyt1t2所有内容加上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 duplicates
      SELECT name FROM t2
     ) n LEFT JOIN
     t1
     ON t1.name = n.name LEFT JOIN
     t2
     ON 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) tmp
 LEFT JOIN t1 ON t1.id = tmp.id
 LEFT JOIN t2 ON t2.id = tmp.id;

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

说明:

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

 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

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


#8楼

我修复了响应,并包括了所有行(基于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
    );

#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 简化 ,通过在查询的FROMON子句中切换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 
    *
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);

#13楼

SQL标准说full join on是行full join oninner 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 onunion all行, right join onunion all行不在inner join oninner join on 。 或者,如果你知道你的inner join on结果不能在一个特定的右表列空,则“ right join on行没有inner join on ”都行中right join onon条件的延长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 JOINRIGHT 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`;

您可以将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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值