Ich möchte einen vollständigen äußeren Join in MySQL durchführen. Ist das möglich? Wird ein Full Outer Join von MySQL unterstützt?
Ich möchte einen vollständigen äußeren Join in MySQL durchführen. Ist das möglich? Wird ein Full Outer Join von MySQL unterstützt?
Antworten:
Sie haben keine FULL JOINS unter MySQL, aber Sie können sie sicher emulieren .
Für einen Code SAMPLE, der von dieser SO-Frage transkribiert wurde , haben Sie:
mit zwei Tabellen 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
Die obige Abfrage funktioniert in besonderen Fällen, in denen eine FULL OUTER JOIN-Operation keine doppelten Zeilen erzeugen würde. Die obige Abfrage hängt vom UNION
Set-Operator ab, um doppelte Zeilen zu entfernen, die durch das Abfragemuster eingeführt wurden. Wir können die Einführung doppelter Zeilen vermeiden, indem wir für die zweite Abfrage ein Anti-Join- Muster verwenden und dann einen UNION ALL-Mengenoperator verwenden, um die beiden Mengen zu kombinieren. Im allgemeineren Fall, in dem ein FULL OUTER JOIN doppelte Zeilen zurückgeben würde, können wir dies tun:
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
und keine doppelten Zeilen vorhanden sind t2
, gibt die Abfrage in dieser Antwort eine Ergebnismenge zurück, die FULL OUTER JOIN emuliert. Im allgemeineren Fall enthält die SELECT-Liste jedoch nicht genügend Spalten / Ausdrücke, um die zurückgegebenen Zeilen eindeutig zu machen. Dann reicht dieses Abfragemuster nicht aus , um die Menge zu reproduzieren, die von a erzeugt würde FULL OUTER JOIN
. Um eine genauere Emulation zu erhalten, benötigen wir einen UNION ALL
Set-Operator, und eine der Abfragen benötigt ein Anti-Join- Muster. Der Kommentar von Pavle Lekic (oben) gibt das richtige Abfragemuster an.
Die Antwort, die Pablo Santa Cruz gab, ist richtig; Falls jedoch jemand auf diese Seite gestoßen ist und weitere Erläuterungen wünscht, finden Sie hier eine detaillierte Aufschlüsselung.
Angenommen, wir haben die folgenden Tabellen:
-- t1
id name
1 Tim
2 Marta
-- t2
id name
1 Tim
3 Katarina
Eine innere Verbindung wie diese:
SELECT *
FROM `t1`
INNER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
Wir würden nur Datensätze erhalten, die in beiden Tabellen wie folgt erscheinen:
1 Tim 1 Tim
Innere Verknüpfungen haben keine Richtung (wie links oder rechts), da sie explizit bidirektional sind - wir benötigen eine Übereinstimmung auf beiden Seiten.
Äußere Verknüpfungen dienen andererseits zum Auffinden von Datensätzen, die möglicherweise nicht mit der anderen Tabelle übereinstimmen. Daher müssen Sie angeben, auf welcher Seite des Joins ein Datensatz fehlen darf.
LEFT JOIN
und RIGHT JOIN
sind Abkürzung für LEFT OUTER JOIN
und RIGHT OUTER JOIN
; Ich werde ihre vollständigen Namen unten verwenden, um das Konzept der äußeren Verknüpfungen gegenüber den inneren Verknüpfungen zu verstärken.
Eine linke äußere Verbindung wie folgt:
SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
... würde uns alle Datensätze aus der linken Tabelle holen, unabhängig davon, ob sie eine Übereinstimmung in der rechten Tabelle haben oder nicht, wie folgt:
1 Tim 1 Tim
2 Marta NULL NULL
Eine rechte äußere Verbindung wie folgt:
SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
... würde uns alle Datensätze aus der rechten Tabelle holen, unabhängig davon, ob sie eine Übereinstimmung in der linken Tabelle haben oder nicht, wie folgt:
1 Tim 1 Tim
NULL NULL 3 Katarina
Eine vollständige äußere Verknüpfung würde uns alle Datensätze aus beiden Tabellen geben, unabhängig davon, ob sie in der anderen Tabelle übereinstimmen oder nicht, mit NULL-Werten auf beiden Seiten, auf denen keine Übereinstimmung vorliegt. Das Ergebnis würde folgendermaßen aussehen:
1 Tim 1 Tim
2 Marta NULL NULL
NULL NULL 3 Katarina
Wie Pablo Santa Cruz jedoch betonte, unterstützt MySQL dies nicht. Wir können es emulieren, indem wir eine UNION aus einem linken und einem rechten Join wie folgt durchführen:
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`;
Sie können sich a UNION
als "beide Abfragen ausführen und dann die Ergebnisse übereinander stapeln" vorstellen. Einige der Zeilen stammen aus der ersten Abfrage, andere aus der zweiten.
Es sollte beachtet werden, dass a UNION
in MySQL exakte Duplikate eliminiert: Tim würde in beiden Abfragen hier erscheinen, aber das Ergebnis der UNION
nur listet ihn einmal auf. Mein Datenbank-Guru-Kollege ist der Meinung, dass man sich nicht auf dieses Verhalten verlassen sollte. Um dies genauer zu beschreiben, könnten wir WHERE
der zweiten Abfrage eine Klausel hinzufügen :
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;
Auf der anderen Seite, wenn Sie wollten um Duplikate sehen aus irgendeinem Grunde, könnten Sie verwenden UNION ALL
.
FULL OUTER JOIN
. Es ist nichts Falsches daran, Abfragen auf diese Weise durchzuführen und UNION zum Entfernen dieser Duplikate zu verwenden. Aber um a wirklich zu replizieren FULL OUTER JOIN
, benötigen wir eine der Abfragen, um ein Anti-Join zu sein.
UNION
Operation diese Duplikate entfernt; Es werden jedoch auch ALLE doppelten Zeilen entfernt, einschließlich doppelter Zeilen, die von einem FULL OUTER JOIN zurückgegeben werden. Zum Emulieren a FULL JOIN b
ist das richtige Muster (a LEFT JOIN b) UNION ALL (b ANTI JOIN a)
.
Durch die Verwendung einer union
Abfrage werden Duplikate entfernt. Dies unterscheidet sich von dem Verhalten, bei full outer join
dem niemals Duplikate entfernt werden:
[Table: t1] [Table: t2]
value value
------- -------
1 1
2 2
4 2
4 5
Dies ist das erwartete Ergebnis von full outer join
:
value | value
------+-------
1 | 1
2 | 2
2 | 2
Null | 5
4 | Null
4 | Null
Dies ist das Ergebnis der Verwendung von left
und right Join
mit union
:
value | value
------+-------
Null | 5
1 | 1
2 | 2
4 | Null
Meine vorgeschlagene Abfrage lautet:
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
Ergebnis der obigen Abfrage, das dem erwarteten Ergebnis entspricht:
value | value
------+-------
1 | 1
2 | 2
2 | 2
4 | NULL
4 | NULL
NULL | 5
@Steve Chambers : [Aus Kommentaren, vielen Dank!]
Hinweis: Dies ist möglicherweise die beste Lösung, sowohl für die Effizienz als auch für die Generierung der gleichen Ergebnisse wie aFULL OUTER JOIN
. Dieser Blog-Beitrag erklärt es auch gut - um aus Methode 2 zu zitieren: "Dies behandelt doppelte Zeilen korrekt und enthält nichts, was es nicht sollte. Es ist notwendig,UNION ALL
anstelle von einfach zu verwendenUNION
, wodurch die Duplikate beseitigt würden, die ich behalten möchte kann bei großen Ergebnismengen erheblich effizienter sein, da keine Duplikate sortiert und entfernt werden müssen. "
Ich habe beschlossen, eine weitere Lösung hinzuzufügen, die aus full outer join
Visualisierung und Mathematik stammt. Es ist nicht besser als oben, aber besser lesbar:
Vollständige äußere Verknüpfung bedeutet
(t1 ∪ t2)
: all int1
oder int2
(t1 ∪ t2) = (t1 ∩ t2) + t1_only + t2_only
: all in beident1
undt2
plus all int1
, die nicht int2
und plus all int2
, die nicht int1
:
-- (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
. Dieser Blog-Beitrag erklärt es auch gut - um aus Methode 2 zu zitieren: "Dies behandelt doppelte Zeilen korrekt und enthält nichts, was es nicht sollte. Es ist notwendig, UNION ALL anstelle von einfachem UNION zu verwenden, wodurch die von mir gewünschten Duplikate entfernt würden Dies kann bei großen Ergebnismengen erheblich effizienter sein, da keine Duplikate sortiert und entfernt werden müssen. "
MySql hat keine FULL-OUTER-JOIN-Syntax. Sie müssen emulieren, indem Sie sowohl LEFT JOIN als auch RIGHT JOIN wie folgt ausführen:
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
MySql verfügt jedoch auch nicht über eine RIGHT JOIN-Syntax. Gemäß der Vereinfachung der äußeren Verknüpfung von MySql wird die rechte Verknüpfung durch Umschalten von t1 und t2 in der Klausel FROM
und ON
in der Abfrage in die entsprechende linke Verknüpfung konvertiert . Daher übersetzt das MySQL-Abfrageoptimierungsprogramm die ursprüngliche Abfrage in Folgendes:
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t2
LEFT JOIN t1 ON t2.id = t1.id
Jetzt kann es nicht schaden, die ursprüngliche Abfrage so zu schreiben, wie sie ist. Wenn Sie jedoch Prädikate wie die WHERE-Klausel haben, bei der es sich um ein Prädikat vor dem Join handelt, oder ein UND-Prädikat für die ON
Klausel, bei der es sich um ein Prädikat beim Join handelt , dann sind Sie es Vielleicht möchten Sie einen Blick auf den Teufel werfen. Das ist im Detail.
Das MySQL-Abfrageoptimierungsprogramm überprüft die Prädikate routinemäßig, wenn sie nicht zurückgewiesen werden . Wenn Sie nun den RIGHT JOIN ausgeführt haben, aber mit dem WHERE-Prädikat für die Spalte von t1, besteht möglicherweise das Risiko, dass Sie auf ein Szenario mit Null-Zurückweisung stoßen.
Zum Beispiel die folgende Abfrage -
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'
wird vom Query Optimizer ins Folgende übersetzt:
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'
Die Reihenfolge der Tabellen hat sich also geändert, aber das Prädikat wird immer noch auf t1 angewendet, aber t1 befindet sich jetzt in der 'ON'-Klausel. Wenn t1.col1 als NOT NULL
Spalte definiert ist , wird diese Abfrage null zurückgewiesen .
Jeder äußere Join (links, rechts, voll), der null abgelehnt wird, wird von MySql in einen inneren Join konvertiert.
Daher können die erwarteten Ergebnisse völlig anders ausfallen als die von MySql zurückgegebenen. Sie könnten denken, es ist ein Fehler mit MySqls RIGHT JOIN, aber das ist nicht richtig. So funktioniert der MySql-Abfrageoptimierer. Daher muss der verantwortliche Entwickler diese Nuancen bei der Erstellung der Abfrage berücksichtigen.
In SQLite sollten Sie Folgendes tun:
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
Keine der obigen Antworten ist tatsächlich richtig, da sie bei doppelten Werten nicht der Semantik folgen.
Für eine Abfrage wie (aus diesem Duplikat ):
SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.Name = t2.Name;
Das richtige Äquivalent ist:
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;
Wenn dies erforderlich ist, um mit NULL
Werten zu arbeiten (was möglicherweise auch erforderlich ist), verwenden Sie den NULL
Vergleichsoperator -safe <=>
anstelle von =
.
FULL OUTER JOIN
wenn die name
Spalte null ist. Die union all
Abfrage mit Anti-Join-Muster sollte das äußere Join-Verhalten korrekt wiedergeben. Welche Lösung jedoch besser geeignet ist, hängt vom Kontext und den Einschränkungen ab, die für die Tabellen aktiv sind.
union all
dieser Antwort fehlt jedoch ein Anti-Join-Muster in der ersten oder zweiten Abfrage, das vorhandene Duplikate beibehält, aber das Hinzufügen neuer verhindert. Je nach Kontext sind andere Lösungen (wie diese) möglicherweise besser geeignet.
Die Abfrage von shA.t wurde für mehr Klarheit geändert:
-- 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
Was haben Sie über die Cross Join- Lösung gesagt ?
SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2
ON 1=1;
select (select count(*) from t1) * (select count(*) from t2))
Zeilen in der Ergebnismenge erhalten wird.
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
Es ist auch möglich, aber Sie müssen die gleichen Feldnamen in select angeben.
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
Ich korrigiere die Antwort und arbeite mit allen Zeilen (basierend auf der Antwort von 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
denen keine Übereinstimmung vorliegt, tableb
und umgekehrt. Das versuchen Sie UNION ALL
, was nur funktionieren würde, wenn diese beiden Tabellen gleich geordnete Spalten haben, was nicht garantiert ist.
Antworten:
SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.id = t2.id;
Kann wie folgt neu erstellt werden:
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;
Die Verwendung einer UNION- oder UNION ALL-Antwort deckt nicht den Randfall ab, in dem die Basistabellen doppelte Einträge haben.
Erläuterung:
Es gibt einen Randfall, den eine UNION oder UNION ALL nicht abdecken kann. Wir können dies nicht auf MySQL testen, da es keine FULL OUTER JOINs unterstützt, aber wir können dies in einer Datenbank veranschaulichen, die es unterstützt:
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
Die UNION-Lösung:
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
Gibt eine falsche Antwort:
id1 id2
NULL 3
NULL 4
1 NULL
2 NULL
5 5
6 6
Die UNION ALL-Lösung:
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
Ist auch falsch.
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
Während diese Abfrage:
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;
Gibt Folgendes:
id1 id2
1 NULL
2 NULL
NULL 3
NULL 4
5 5
6 6
6 6
6 6
6 6
Die Reihenfolge ist unterschiedlich, entspricht aber ansonsten der richtigen Antwort.
UNION ALL
Lösung falsch dar. Außerdem wird eine Lösung vorgestellt, UNION
die bei großen Quelltabellen aufgrund der erforderlichen Deduplizierung langsamer ist. Schließlich würde es nicht kompiliert werden, da das Feld id
in der Unterabfrage nicht vorhanden ist tmp
.
UNION ALL
Falsche Darstellung: "Die Lösung: ... ist auch falsch." Der von Ihnen präsentierte Code lässt den Schnittpunktausschluss von right-join ( where t1.id1 is null
) aus, der in der Datei angegeben werden muss UNION ALL
. Das heißt, Ihre Lösung übertrifft alle anderen nur dann, wenn eine dieser anderen Lösungen falsch implementiert ist. Auf "Niedlichkeit" Punkt genommen. Das war unentgeltlich, ich entschuldige mich.
Der SQL - Standard sagt full join on
ist inner join on
Reihe union all
von Nullen unerreichte linke Tabellenzeilen erweiterten union all
rechte Tabellenzeilen durch Nullen erweitert. Dh inner join on
Zeilen union all
Zeilen in, left join on
aber nicht inner join on
union all
Zeilen in, right join on
aber nicht inner join on
.
Dh left join on
Reihen union all
right join on
Reihen nicht in inner join on
. Wenn Sie wissen, dass Ihr inner join on
Ergebnis in einer bestimmten rechten Tabellenspalte nicht null enthalten kann, sind " right join on
Zeilen nicht in inner join on
" Zeilen right join on
mit der on
um and
diese Spalte erweiterten Bedingung is null
.
Dh ähnlich right join on
union all
geeignete left join on
Zeilen.
Von Was ist der Unterschied zwischen "INNER JOIN" und "OUTER JOIN"? ::
(SQL Standard 2006 SQL / Foundation 7.7 Syntaxregeln 1, Allgemeine Regeln 1 b, 3 c & d, 5 b.)