MySQLで完全外部結合を実行したい。これは可能ですか?完全外部結合はMySQLでサポートされていますか?
MySQLで完全外部結合を実行したい。これは可能ですか?完全外部結合はMySQLでサポートされていますか?
回答:
MySQLにフルジョインはありませんが、エミュレートできます。
このSOの質問から書き起こされたコードSAMPLEの場合:
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
は、クエリパターンによって導入された重複行を削除するセット演算子に依存しています。2番目のクエリに逆結合パターンを使用し、次に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
(SELECT ... FROM tbl1 LEFT JOIN tbl2 ...) UNION ALL (SELECT ... FROM tbl1 RIGHT JOIN tbl2 ... WHERE tbl1.col IS NULL)
t1
とt2
、FULL OUTERをエミュレート結果セットを返さないこの答えの問合せでは、JOINません。ただし、より一般的なケースでは、たとえば、SELECTリストに返される行を一意にするのに十分な列/式が含まれていない場合、このクエリパターンは、によって生成されるセットを再現するには不十分FULL OUTER JOIN
です。より忠実なエミュレーションを取得するには、UNION ALL
集合演算子が必要であり、クエリの1つには逆結合パターンが必要です。Pavle Lekic(上記)からのコメントは、正しいクエリパターンを示しています。
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`
UNION
SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
a UNION
は「これらの両方のクエリを実行してから、結果を互いに積み重ねる」という意味と考えることができます。一部の行は最初のクエリから取得され、一部は2番目のクエリから取得されます。
UNION
MySQLのa は正確な重複を排除することに注意する必要があります。Timはここで両方のクエリにUNION
表示されますが、唯一の結果は彼を一度だけリストします。私のデータベースの第一人者の同僚は、この振る舞いに依存すべきではないと感じています。したがって、より明確に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
。
FULL OUTER JOIN
。このようにクエリを実行し、UNIONを使用してそれらの重複を削除することには何の問題もありません。ただし、を実際に複製するFULL OUTER JOIN
には、クエリの1つを反結合にする必要があります。
UNION
操作がそれらの重複を削除することは事実です。ただし、FULL OUTER JOINによって返される重複行を含むすべての重複行も削除されます。エミュレートするa FULL JOIN b
ための正しいパターンは(a LEFT JOIN b) UNION ALL (b ANTI JOIN a)
です。
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
@Steve Chambers:[コメントから、多くの感謝を込めて!]
注:これは、効率とaと同じ結果を生成するための最良の解決策になる可能性があります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)
FULL OUTER JOIN
です。このブログ投稿では、それについても詳しく説明しています-方法2から引用すると、「これは重複行を正しく処理し、不要なものは含まれていません。通常のUNIONではなくUNION ALLを使用する必要があります。これにより、重複を排除できます。保持します。重複したものを並べ替えたり削除したりする必要がないため、これは大きな結果セットで非常に効率的です。」
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
and 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
である句、中、参加述語、あなたを悪魔を見てみたいかもしれません。詳細です。
MySqlクエリオプティマイザーは、述語がnull-rejectedであるかどうかを定期的にチェックします。 ここで、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-rejectedになります。
null拒否された外部結合(左、右、完全)は、MySqlによって内部結合に変換されます。
したがって、期待する結果は、MySqlが返す結果とは完全に異なる可能性があります。MySqlのRIGHT JOINのバグだと思うかもしれませんが、それは正しくありません。MySqlクエリオプティマイザーが機能するちょうどその方法。そのため、担当の開発者は、クエリを作成するときにこれらのニュアンスに注意を払う必要があります。
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
重複する値がある場合、上記の答えは実際には正しいものではありません。
(この複製から)などのクエリの場合:
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比較演算子を使用し=
ます。
FULL OUTER JOIN
、name
列がnullの場合は常にaとは異なる結果になる可能性があります。union all
抗加入パターンを持つクエリは、外側が正しく動作に参加再現する必要がありますが、そのソリューションは、より適切なコンテキストにし、テーブル上でアクティブな制約に依存しています。
union all
が、その回答は、既存の重複を保持するが、新しいものを追加できないようにする最初または2番目のクエリの逆結合パターンを見逃しています。コンテキストによっては、他のソリューション(このソリューションなど)の方が適切な場合があります。
より明確にするために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 t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2
ON 1=1;
select (select count(*) from t1) * (select count(*) from t2))
結果セットの行と組み合わせます。
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
);
tablea
、一致がない行のみを返し、tableb
その逆も同様です。しようとするとUNION ALL
、これらの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またはUNION ALLの回答を使用しても、ベーステーブルのエントリが重複しているという特殊なケースには対応できません。
説明:
UNIONまたはUNION ALLではカバーできないエッジケースがあります。FULL OUTER JOINをサポートしていないため、これを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
順序は異なりますが、それ以外は正解と一致します。
UNION ALL
解決策を偽っています。また、UNION
必要な重複除外のため、大規模なソーステーブルでは速度が遅くなるソリューションを示します。最後に、フィールドid
がサブクエリに存在しないため、コンパイルされませんtmp
。
UNION ALL
解決策:...も不正解です。」提示するコードwhere t1.id1 is null
では、で指定する必要があるright-join()からの交差除外を除外していUNION ALL
ます。つまり、他のソリューションの1つが正しく実装されていない場合にのみ、ソリューションが他のすべてのソリューションよりも優先されます。「かわいさ」についてポイントをとった。それは無意味でした、私の謝罪。
SQL標準は言うfull join on
あるinner join on
行union all
ヌルによって拡張比類のない左の表の行は、union all
右の表の行がヌルによって拡張しました。つまり、inner join on
行union all
はあるleft join on
が、inner join on
union all
行はright join on
ないがinner join on
。
つまり、にないleft join on
行union all
right join on
rows inner join on
です。またはinner join on
、特定の右側のテーブル列で結果がnullにならないことがわかっている場合、「にright join on
含まれないinner join on
行」は、その列によって条件が拡張されright join on
た行です。on
and
is null
つまり、同様にright join on
union all
適切なleft join on
行。
「INNER JOINを」と「OUTER JOINを」の違いは何ですか?:
(SQL Standard 2006 SQL / Foundation 7.7構文規則1、一般規則1 b、3 cおよびd、5 b。)