it-swarm.com.de

Wie mache ich einen FULL OUTER JOIN in MySQL?

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?

581
Spencer

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
591

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.

Beispieltabellen

Angenommen, wir haben die folgenden Tabellen:

-- t1
id  name
1   Tim
2   Marta

-- t2
id  name
1   Tim
3   Katarina

Inner Joins

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

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.

Linke äußere Verbindung

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

Right Outer Join

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

Vollständige äußere Verbindung

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 .

334
Nathan Long

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

[SQL Fiddle]

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

[SQL Fiddle]


@ 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 ein FULL 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 plain UNION, 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 in t1 oder in t2
(t1 ∪ t2) = (t1 ∩ t2) + t1_only + t2_only: alle in t1 und t2 sowie alle in t1, die nicht in t2 und alle in t2, die nicht in t1 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)

[SQL Fiddle]

30
shA.t

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

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

4
Gordon Linoff

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. Null-Rejected Definition and Examples 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.

4
Akshayraj Kore

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);
3
lolo

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

was sagst du zu Cross Join Lösung?

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