Ich möchte einen Full Outer Join in MySQL durchführen. Ist das möglich? Wird eine vollständige äußere Verknüpfung von MySQL unterstützt?
Sie haben keine FULL JOINS auf MySQL, aber Sie können sicher sein, dass sie emulieren .
Für einen Code SAMPLE transkribiert von diese SO Frage 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 erzeugt. Die obige Abfrage hängt vom Operator UNION
ab, um doppelte Zeilen zu entfernen, die durch das Abfragemuster eingefügt wurden. Wir können doppelte 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
Die Antwort, die Pablo Santa Cruz gab, ist richtig; Für den Fall, dass jemand auf dieser Seite gestolpert 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 folgt:
SELECT *
FROM `t1`
INNER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
Wir würden nur Datensätze erhalten, die in beiden Tabellen wie folgt vorkommen:
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.
Outer-Joins hingegen dienen 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ürzungen für LEFT OUTER JOIN
und RIGHT OUTER JOIN
; Ich werde ihre vollständigen Namen unten verwenden, um das Konzept der äußeren und inneren Verknüpfungen zu verstärken.
Eine linke äußere Verknüpfung, wie folgt:
SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
... würde uns alle Datensätze aus der linken Tabelle abrufen, unabhängig davon, ob sie in der rechten Tabelle übereinstimmen oder nicht:
1 Tim 1 Tim
2 Marta NULL NULL
Ein rechter äußerer Join wie dieser:
SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
... würde uns alle Datensätze aus der rechten Tabelle abrufen, unabhängig davon, ob sie in der linken Tabelle übereinstimmen oder nicht:
1 Tim 1 Tim
NULL NULL 3 Katarina
Ein vollständiger Outer Join würde uns alle Datensätze aus beiden Tabellen liefern, unabhängig davon, ob sie in der anderen Tabelle übereinstimmen oder nicht, mit NULL-Werten auf beiden Seiten, auf denen keine Übereinstimmung besteht. Das Ergebnis würde so aussehen:
1 Tim 1 Tim
2 Marta NULL NULL
NULL NULL 3 Katarina
Wie Pablo Santa Cruz betonte, unterstützt MySQL dies jedoch nicht. Wir können es emulieren, indem wir eine UNION aus einem linken und einem rechten Join erstellen:
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 UNION
als "beide Abfragen ausführen und dann die Ergebnisse übereinander stapeln" vorstellen. Einige der Zeilen stammen aus der ersten und andere aus der zweiten Abfrage.
Es sollte beachtet werden, dass ein UNION
in MySQL exakte Duplikate eliminiert: Tim würde in beiden Abfragen hier erscheinen, aber das Ergebnis des UNION
listet ihn nur einmal auf. Mein Datenbank-Guru-Kollege ist der Ansicht, dass sich dieses Verhalten nicht auf etwas stützen sollte. Um genauer zu sein, könnten wir der zweiten Abfrage eine WHERE
-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 können Sie UNION ALL
verwenden, wenn Sie aus irgendeinem Grund Duplikate sehen möchten .
Durch die Verwendung einer union
Abfrage werden Duplikate entfernt. Dies unterscheidet sich vom Verhalten von full outer join
, bei 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 entspricht dem erwarteten Ergebnis:
value | value
------+-------
1 | 1
2 | 2
2 | 2
4 | NULL
4 | NULL
NULL | 5
@ Steve Chambers : [Von Kommentaren mit vielen Dank!]
Hinweis: Dies ist möglicherweise die beste Lösung, sowohl aus Gründen der Effizienz als auch zur Erzielung der gleichen Ergebnisse wie einFULL OUTER JOIN
. Dieser Blogbeitrag 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
zu verwenden. anstelle von plainUNION
, wodurch die Duplikate entfernt würden, die ich behalten möchte. Dies kann bei großen Ergebnismengen erheblich effizienter sein, da Duplikate nicht sortiert und entfernt werden müssen. "
Ich habe beschlossen, eine andere 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)
: alles int1
oder int2
(t1 ∪ t2) = (t1 ∩ t2) + t1_only + t2_only
: alle int1
undt2
sowie alle int1
, die nicht int2
und alle int2
, die nicht int1
sind:
-- (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)
In SQLite sollten Sie dies 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 korrekt, da sie bei doppelten Werten nicht der Semantik folgen.
Für eine Abfrage wie (von 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 Sie dafür NULL
Werte verwenden müssen (was auch erforderlich sein kann), verwenden Sie den sicheren Vergleichsoperator NULL
, <=>
anstelle von =
.
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 hat aber auch keine RIGHT JOIN-Syntax. Gemäß MySqls Vereinfachung der äußeren Verknüpfung wird die rechte Verknüpfung in die äquivalente linke Verknüpfung konvertiert, indem in der Abfrage in der Klausel FROM
und ON
t1 und t2 vertauscht werden. 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
Es schadet jetzt nicht, die ursprüngliche Abfrage so zu schreiben, wie sie ist, aber sagen Sie, wenn Sie Prädikate wie die WHERE-Klausel haben, die ein before-join Prädikat oder ein AND-Prädikat ist In der ON
-Klausel, die ein während des Join Prädikat ist, möchten Sie vielleicht einen Blick auf den Teufel werfen. Das liegt im Detail.
Der MySql-Abfrageoptimierer überprüft die Prädikate routinemäßig, ob sie null-zurückgewiesen sind. Wenn Sie nun RIGHT JOIN ausgeführt haben, aber das WHERE-Prädikat für die Spalte von t1 hat, besteht die Gefahr, dass Sie auf ein null-zurückgewiesenes Szenario 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 Abfrageoptimierer in die folgende Sprache ü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 weiterhin auf t1 angewendet, aber t1 befindet sich jetzt in der 'ON'-Klausel. Wenn t1.col1 als NOT NULL
-Spalte definiert ist, wird diese Abfrage mit Null zurückgewiesen .
Jeder äußere Join (left, right, full), der null-verworfen ist, wird von MySql in einen inneren Join konvertiert.
Daher können die erwarteten Ergebnisse völlig anders sein 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 beim Erstellen der Abfrage auf diese Nuancen achten.
Sie können Folgendes tun:
(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);
Die Abfrage von shA.t wurde geändert, um mehr Klarheit zu schaffen:
-- 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
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
was sagst du zu Cross Join Lösung?
SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2
ON 1=1;