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


当前回答

使用交叉联接解决方案:

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

其他回答

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

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

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和ON子句中的t1和t2,将右连接转换为等效的左连接。因此,MySQL查询优化器将原始查询转换为以下内容-

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'

由查询优化器转换为以下内容:

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拒绝。

任何被null拒绝的外部联接(左、右、全)都会被MySQL转换为内部联接。

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

Use:

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上测试这一点,因为它不支持完整的外部连接,但我们可以在一个支持它的数据库上说明这一点:

 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;

这给了我们这样的答案:

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

顺序不同,但在其他方面与正确答案匹配。

为了更清晰,我修改了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