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