MySQLでフルアウタージョインをしたいのですが。これは可能ですか?完全外部結合はMySQLでサポートされていますか?
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
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
完全外部結合では、他のテーブルと一致するかどうかにかかわらず、両方のテーブルからのすべてのレコードが得られます。結果は次のようになります。
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
を使用できます。
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
私が提案する質問は:
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
@スティーブチェンバーズ : [コメントから、どうもありがとう!]
注: これは、効率性とFULL OUTER JOIN
と同じ結果を生成するための両方のための最善の解決策です。 このブログ記事 それもうまく説明しています - 方法2から引用すると:"これは重複行を正しく処理し、本来あるべきものを含みません。普通のUNION
ではなくUNION ALL
を使う必要があります。大規模な結果セットでは、重複を並べ替えたり削除したりする必要がないので、これは非常に効率的です。 "
私は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)
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
上記の答えはどれも実際には正しいものではありません。値が重複している場合、それらは意味論に従わないからです。
(こちらから 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比較演算子=
を使用してください。
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であるかどうかを定期的にチェックします。 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クエリオプティマイザの働きです。そのため、開発担当者は、クエリを作成するときにこれらの微妙な違いに注意を払う必要があります。
より明確にするために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
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);
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
Cross join solutionについてどう思いますか?
SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2
ON 1=1;