我想在MySQL中进行完全外部联接。 这可能吗? MySQL是否支持完全外部联接?
在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
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
也可以,但必须在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
使用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]
修改了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
上述答案实际上都不是正确的,因为当存在重复的值时,它们不遵循语义。
对于诸如(来自此重复项 )的查询:
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比较运算符<=>
而不是=
。
回答:
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
顺序不同,但是与正确答案匹配。
我修复了响应,并包括了所有行(基于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
);
您对交叉联接解决方案怎么说?
SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2
ON 1=1;
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查询优化器的工作方式。 因此,负责开发人员在构建查询时必须注意这些细微差别。
您在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
您可以执行以下操作:
(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);
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。)
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
。