web-dev-qa-db-ja.com

MySQLでFULL OUTER JOINをするには?

MySQLでフルアウタージョインをしたいのですが。これは可能ですか?完全外部結合はMySQLでサポートされていますか?

550
Spencer

MySQLにはFULL JOINSはありませんが、 エミュレートすることはできます

SAMPLEが から転記されたコードの場合、このSO question になります。

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

上記のクエリは、FULL OUTER JOIN操作で重複行が生成されないという特別な場合に機能します。上記のクエリは、クエリパターンによって導入された重複行を削除するためのUNION set演算子に依存します。 2番目のクエリに anti-join パターンを使用し、次にUNION ALLセット演算子を使用して2つのセットを結合することで、重複行が発生しないようにすることができます。より一般的なケースでは、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
556

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

フルアウタージョイン

完全外部結合では、他のテーブルと一致するかどうかにかかわらず、両方のテーブルからのすべてのレコードが得られます。結果は次のようになります。

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は、「これら両方のクエリを実行してから、結果を互いの上に重ねる」ことを意味すると考えることができます。行の一部は最初のクエリから取得され、一部は2番目のクエリから取得されます。

MySQLのUNIONは正確な重複を排除することに注意してください:Timはここの両方の問い合わせに現れますが、UNIONの結果は彼を一度だけリストします。私のデータベースの達人の同僚は、この振る舞いは信頼されるべきではないと感じています。もっと明確にするために、2番目のクエリに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を使用できます。

323
Nathan Long

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]


@スティーブチェンバーズ : [コメントから、どうもありがとう!]
注: これは、効率性とFULL OUTER JOINと同じ結果を生成するための両方のための最善の解決策です。 このブログ記事 それもうまく説明しています - 方法2から引用すると:"これは重複行を正しく処理し、本来あるべきものを含みません。普通のUNIONではなくUNION ALLを使う必要があります。大規模な結果セットでは、重複を並べ替えたり削除したりする必要がないので、これは非常に効率的です。 "


私はfull outer joinの視覚化と数学から来る別の解決策を追加することにしました、それは上のものより良くありませんが、より読みやすいです:

完全外部結合は(t1 ∪ t2)を意味します:すべてt1またはt2
(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]

29
shA.t

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
4
Rami Jamleh

上記の答えはどれも実際には正しいものではありません。値が重複している場合、それらは意味論に従わないからです。

(こちらから duplicate )のようなクエリの場合:

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

4
Gordon Linoff

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 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-rejectedであるかどうかを定期的にチェックします。 Null-Rejected Definition and Examples RIGHT JOINを実行したが、t1からの列にWHERE述語を使用した場合は、null-rejectedシナリオに遭遇する危険性があります。

たとえば、次のクエリ -

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-rejectedになります。

null-rejectedであるすべての外部結合(left、right、full)は、MySqlによって内部結合に変換されます。

したがって、期待する結果は、MySqlが返す結果とはまったく異なる可能性があります。あなたはそれがMySqlのRIGHT JOINのバグだと思うかもしれませんが、それは正しくありません。それこそがMySqlクエリオプティマイザの働きです。そのため、開発担当者は、クエリを作成するときにこれらの微妙な違いに注意を払う必要があります。

4
Akshayraj Kore

より明確にするために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 
3
a20

次のことができます。

(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);
2
lolo
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
0
Alex Pliutau

Cross join solutionについてどう思いますか?

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