782

MySQLで完全な外部結合を実行したいと思います。これは可能ですか?完全な外部結合はMySQLでサポートされていますか?

4

15 に答える 15

806

MySQLには完全結合はありませんが、確実にエミュレートできます

このStackOverflowの質問から転記されたコードサンプルの場合、次のようになります。

2つのテーブル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

上記のクエリは、完全外部結合操作で重複行が生成されない特殊なケースで機能します。上記のクエリはUNION、クエリパターンによって導入された重複行を削除するために集合演算子に依存しています。2番目のクエリに反結合パターンを使用して重複行を導入することを回避し、次にUNIONALL集合演算子を使用して2つの集合を結合します。より一般的なケースでは、完全外部結合が重複行を返す場合、これを行うことができます。

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
于 2011-01-25T17:38:14.673 に答える
406

パブロサンタクルスが出した答えは正しいです。ただし、誰かがこのページを偶然見つけて、さらに明確にしたい場合に備えて、ここに詳細な内訳を示します。

表の例

次のテーブルがあるとします。

-- 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 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`;

UNIONaは、「これらのクエリの両方を実行してから、結果を互いに積み重ねる」という意味と考えることができます。一部の行は最初のクエリから取得され、一部は2番目のクエリから取得されます。

UNIONMySQLのaは正確な重複を排除することに注意してください。Timはここの両方のクエリに表示されますが、結果UNIONは彼を1回だけリストします。私のデータベースの第一人者の同僚は、この振る舞いに頼るべきではないと感じています。したがって、それについてより明確にするWHEREために、2番目のクエリに句を追加できます。

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場合は、を使用できます。

于 2012-02-09T16:23:09.983 に答える
45

ユニオンクエリを使用すると重複が削除されます。これは、重複を削除しない完全外部結合の動作とは異なります。

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

これは、完全な外部結合の予想される結果です。

value | value
------+-------
1     | 1
2     | 2
2     | 2
Null  | 5
4     | Null
4     | Null

これは、ユニオンで結合と右結合を使用した結果です。

value | value
------+-------
Null  | 5
1     | 1
2     | 2
4     | Null

SQLフィドル

私の提案するクエリは次のとおりです。

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フィドル


@Steve Chambers[コメントから、ありがとうございました!]

注:これは、効率とと同じ結果を生成するための両方の点で最良の解決策である可能性がありますFULL OUTER JOINこのブログ投稿もそれをよく説明しています-方法2から引用します:「これは重複行を正しく処理し、すべきでないものは何も含まれていません。UNION ALLプレーンの代わりに使用する必要がありますUNION。これにより、保持したい重複がなくなります。これは重複を並べ替えて削除する必要がないため、大きな結果セットでは大幅に効率が上がる可能性があります。」


完全な外部結合の視覚化と数学から得られる別のソリューションを追加することにしました。上記よりも優れているわけではありませんが、より読みやすくなっています。

完全な外部結合とは、 (t1 ∪ t2):all int1またはin t2 (t1 ∪ t2) = (t1 ∩ t2) + t1_only + t2_only:all inbotht1およびt2plusallin t1that is not in t2and plus all in t2that is not in 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フィドル

于 2015-05-28T05:01:38.677 に答える
10

MySQLにはFULL-OUTER-JOIN構文がありません。次のように、LEFTJOINとRIGHTJOINの両方を実行してエミュレートする必要があります。

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

ただし、MySQLにはRIGHTJOIN構文もありません。MySQLの外部結合の簡略化FROMによると、クエリのandON句の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述部を使用した場合、nullが拒否されたシナリオに遭遇するリスクがある可能性があります。

たとえば、クエリ

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のRIGHTJOINのバグだと思うかもしれませんが、そうではありません。MySQLクエリオプティマイザがどのように機能するか。したがって、担当の開発者は、クエリを作成するときにこれらのニュアンスに注意を払う必要があります。

于 2017-10-12T19:22:27.520 に答える
7

重複する値がある場合、それらはセマンティクスに従わないため、前の回答はどれも実際には正しくありません。

(この複製から)のようなクエリの場合:

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比較演算子を使用してください。<=>=

于 2017-01-14T13:27:01.157 に答える
4

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
于 2013-01-14T13:50:31.940 に答える
4

より明確にするために、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 
于 2016-03-11T08:47:04.733 に答える
4

次のことができます。

(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);
于 2018-06-05T07:54:37.803 に答える
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
于 2013-10-24T11:10:07.627 に答える
1

完全な外部結合を変換できます。例:

SELECT fields
FROM firsttable
FULL OUTER JOIN secondtable ON joincondition

の中へ:

SELECT fields
FROM firsttable
LEFT JOIN secondtable ON joincondition
UNION ALL
SELECT fields (replacing any fields from firsttable with NULL)
FROM secondtable
WHERE NOT EXISTS (SELECT 1 FROM firsttable WHERE joincondition)

または、少なくとも1つの列がある場合、たとえばfoofirsttableそれがNULLでない場合は、次のように実行できます。

SELECT fields
FROM firsttable
LEFT JOIN secondtable ON joincondition
UNION ALL
SELECT fields
FROM firsttable
RIGHT JOIN secondtable ON joincondition
WHERE firsttable.foo IS NULL
于 2020-08-17T21:41:20.923 に答える
-1

私は応答を修正し、作業にはすべての行が含まれます(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
    );
于 2017-05-22T21:53:33.593 に答える
-2

使用する:

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またはUNIONALLの回答を使用しても、ベーステーブルのエントリが重複しているエッジケースはカバーされません。

説明:

UNIONまたはUNIONALLではカバーできないエッジケースがあります。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;

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

順序は異なりますが、それ以外は正解と一致します。

于 2017-02-09T14:29:57.877 に答える
-2

クロスジョインソリューションを使用します。

SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2 
ON 1=1;
于 2017-06-13T19:53:53.590 に答える
-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
于 2014-02-03T12:30:07.527 に答える
-5

SQL標準によるfull join onと、一致しない左側のテーブル行がnullで拡張され、inner join on右側のテーブル行がnullで拡張されます。つまり、行は行になりますが、行はありません。union allunion allinner join onunion allleft join oninner join on union allright join oninner join on

つまり、left join onunion all right join onはにありませんinner join on。またはinner join on、特定の右側のテーブル列で結果にnullを含めることができないことがわかっている場合、「right join on行ではないinner join on」は、その列によって条件が拡張されright join onた行です。onandis null

つまり、同様にright join on union all適切なleft join on行です。

「 INNERJOIN 」と「OUTERJOIN」の違いは何ですか?

(SQL Standard 2006 SQL / Foundation 7.7構文規則1、一般規則1 b、3 c&d、5 b)

于 2018-08-11T22:09:28.417 に答える