FULL OUTER JOIN emulieren, wenn native Unterstützung fehlt
AI generated
SELECT
JOIN
SQL / Joins
FULL OUTER JOIN emulieren ohne native Unterstützung
Wie UNION aus LEFT JOIN und RIGHT JOIN einen FULL OUTER JOIN korrekt nachbildet

Ein FULL OUTER JOIN liefert alle Zeilen aus beiden beteiligten Tabellen, unabhängig davon, ob ein passendes Gegenstück auf der jeweils anderen Seite existiert, und füllt fehlende Werte mit NULL auf. Nicht jede Datenbank unterstützt diese Operation jedoch nativ: MySQL kannte bis vor kurzem keinen eigenen FULL-JOIN-Syntax, und auch in älteren Versionen anderer Systeme fehlt die Funktion teilweise vollständig. Dieser Artikel zeigt, wie sich ein FULL OUTER JOIN zuverlässig über eine Kombination aus LEFT JOIN, RIGHT JOIN und UNION nachbilden lässt, welche Alternative mit LEFT JOIN und NOT EXISTS in bestimmten Fällen vorzuziehen ist, und wo typische Korrektheits- und Performance-Fallen bei der Emulation lauern.

10 Min. Lesezeit FULL OUTER JOIN Join-Emulation

1. Was ein FULL OUTER JOIN eigentlich liefert

Ein FULL OUTER JOIN kombiniert das Verhalten von LEFT JOIN und RIGHT JOIN gleichzeitig: Jede Zeile aus der linken Tabelle erscheint im Ergebnis, unabhängig davon, ob eine passende Zeile in der rechten Tabelle existiert, und ebenso erscheint jede Zeile aus der rechten Tabelle, unabhängig davon, ob eine passende Zeile in der linken Tabelle existiert. Fehlt ein Gegenstück auf einer Seite, werden die entsprechenden Spalten mit NULL aufgefüllt, genau wie bei einem einfachen LEFT oder RIGHT JOIN.

Typische Anwendungsfälle sind Datenabgleiche zwischen zwei Systemen, etwa der Vergleich zwischen einer Bestandsliste im ERP-System und einer Bestandsliste im Warehouse-Management-System, bei dem sowohl Einträge, die nur im einen System existieren, als auch Einträge, die nur im anderen System existieren, sichtbar werden sollen. Ein reiner INNER JOIN würde in diesem Szenario genau die interessantesten Fälle, nämlich die Abweichungen zwischen beiden Systemen, stillschweigend verschlucken.


-- Nativer FULL OUTER JOIN, dort wo verfügbar
SELECT
    erp.product_id AS erp_product_id,
    wms.product_id AS wms_product_id,
    erp.stock_quantity AS erp_quantity,
    wms.stock_quantity AS wms_quantity
FROM erp_inventory erp
FULL OUTER JOIN wms_inventory wms
    ON erp.product_id = wms.product_id;

2. Grundprinzip der Emulation: LEFT JOIN plus RIGHT JOIN per UNION

Fehlt native FULL-JOIN-Unterstützung, lässt sich derselbe Effekt durch die Kombination zweier einfacherer Joins erreichen, die beide praktisch überall verfügbar sind. Ein LEFT JOIN liefert alle Zeilen der linken Tabelle inklusive der nicht passenden mit NULL aufgefüllt, ein zweiter RIGHT JOIN, oder gleichwertig ein LEFT JOIN mit vertauschter Tabellenreihenfolge, liefert entsprechend alle Zeilen der rechten Tabelle. Werden beide Ergebnismengen per UNION zusammengeführt, entsteht in der Summe exakt dasselbe Ergebnis wie bei einem echten FULL OUTER JOIN.

Entscheidend ist dabei die Verwendung von UNION statt UNION ALL, denn die Zeilen, bei denen auf beiden Seiten ein passendes Gegenstück existiert, erscheinen sonst doppelt: einmal aus dem LEFT JOIN und ein zweites Mal identisch aus dem RIGHT JOIN. UNION entfernt diese Duplikate automatisch, sofern die Ergebniszeilen tatsächlich vollständig identisch sind, was bei sauber definierten Spaltenlisten der Regelfall ist.


-- Emulierter FULL OUTER JOIN via LEFT JOIN + RIGHT JOIN + UNION
SELECT
    erp.product_id AS erp_product_id,
    wms.product_id AS wms_product_id,
    erp.stock_quantity AS erp_quantity,
    wms.stock_quantity AS wms_quantity
FROM erp_inventory erp
LEFT JOIN wms_inventory wms ON erp.product_id = wms.product_id

UNION

SELECT
    erp.product_id AS erp_product_id,
    wms.product_id AS wms_product_id,
    erp.stock_quantity AS erp_quantity,
    wms.stock_quantity AS wms_quantity
FROM erp_inventory erp
RIGHT JOIN wms_inventory wms ON erp.product_id = wms.product_id;

3. Alternative: LEFT JOIN kombiniert mit LEFT JOIN und NOT EXISTS

Auf Datenbanken ohne jegliche RIGHT-JOIN-Unterstützung, oder wenn RIGHT JOIN aus Stilgründen im Team vermieden werden soll, lässt sich dieselbe Emulation auch ausschließlich mit LEFT JOIN und einer zusätzlichen NOT-EXISTS-Bedingung formulieren. Der erste Teil liefert wie zuvor alle Zeilen der linken Tabelle inklusive Übereinstimmungen und Nicht-Übereinstimmungen. Der zweite Teil liefert ausschließlich die Zeilen der rechten Tabelle, für die explizit keine passende Zeile in der linken Tabelle existiert.

Diese Variante hat gegenüber der reinen UNION-aus-zwei-Joins-Lösung einen praktischen Vorteil: Da der zweite Teil von vornherein nur die tatsächlich nicht übereinstimmenden Zeilen der rechten Tabelle liefert, ist keine Duplikatentfernung mehr nötig, UNION ALL statt UNION genügt und spart damit den zusätzlichen Sortier- oder Hash-Schritt zur Duplikaterkennung, der bei großen Ergebnismengen spürbar ins Gewicht fallen kann.


-- Emulation ohne RIGHT JOIN, mit UNION ALL statt UNION
SELECT
    erp.product_id AS erp_product_id,
    wms.product_id AS wms_product_id,
    erp.stock_quantity AS erp_quantity,
    wms.stock_quantity AS wms_quantity
FROM erp_inventory erp
LEFT JOIN wms_inventory wms ON erp.product_id = wms.product_id

UNION ALL

SELECT
    NULL AS erp_product_id,
    wms.product_id AS wms_product_id,
    NULL AS erp_quantity,
    wms.stock_quantity AS wms_quantity
FROM wms_inventory wms
WHERE NOT EXISTS (
    SELECT 1 FROM erp_inventory erp
    WHERE erp.product_id = wms.product_id
);

4. Praxisfall: MySQL vor der FULL-JOIN-Unterstützung

MySQL hat über sehr lange Zeit keinerlei native FULL-JOIN-Syntax angeboten, und selbst in aktuellen Versionen bleibt die Unterstützung eingeschränkt gegenüber PostgreSQL, SQL Server und Oracle, wo FULL OUTER JOIN seit jeher zum Standardrepertoire gehört. In produktiven MySQL-Umgebungen, die einen Datenabgleich zwischen zwei Tabellen benötigen, gehört die UNION-Emulation daher zum festen Werkzeugkasten jedes erfahrenen Entwicklerteams, unabhängig davon, ob es sich um ein Legacy-System oder eine bewusste Technologieentscheidung handelt.

Auch andere Datenbanksysteme, insbesondere ältere oder eingebettete Varianten wie frühe SQLite-Versionen, kannten lange Zeit keinen nativen FULL-JOIN-Syntax. Wer Code schreibt, der auf mehreren Datenbanksystemen mit unterschiedlichem Funktionsumfang laufen soll, sollte die Emulation daher grundsätzlich als portable Standardlösung einplanen, statt sich auf eine native FULL-JOIN-Verfügbarkeit zu verlassen, die je nach Zielsystem schlicht nicht gegeben ist.

5. Korrektheitsfalle: WHERE-Bedingungen an der falschen Stelle

Eine der häufigsten Fehlerquellen bei der Emulation entsteht, wenn zusätzliche Filterbedingungen versehentlich in eine WHERE-Klausel statt in die JOIN-Bedingung selbst geschrieben werden. Eine WHERE-Bedingung auf eine Spalte der rechten Tabelle im ersten LEFT-JOIN-Teil filtert unbeabsichtigt genau die Zeilen heraus, die eigentlich mit NULL erhalten bleiben sollten, weil eine WHERE-Klausel erst nach dem Join ausgewertet wird und NULL-Werte in den meisten Vergleichsoperatoren als unbekannt statt als wahr gewertet werden.

Die korrekte Lösung besteht darin, alle fachlichen Filterbedingungen, die sich auf eine einzelne Tabelle beziehen und trotzdem nicht passende Zeilen erhalten sollen, direkt in die ON-Klausel des jeweiligen Joins zu schreiben, statt in eine nachgelagerte WHERE-Klausel. Nur Bedingungen, die tatsächlich beide Ergebnismengen der UNION gemeinsam einschränken sollen, gehören in eine WHERE-Klausel, die auf das Gesamtergebnis der UNION angewendet wird.


-- Falsch: WHERE filtert NULL-Zeilen unbeabsichtigt heraus
SELECT erp.product_id, wms.product_id, wms.stock_quantity
FROM erp_inventory erp
LEFT JOIN wms_inventory wms ON erp.product_id = wms.product_id
WHERE wms.warehouse_code = 'MAIN';  -- entfernt Zeilen ohne WMS-Match!

-- Richtig: Filterbedingung in die ON-Klausel verschieben
SELECT erp.product_id, wms.product_id, wms.stock_quantity
FROM erp_inventory erp
LEFT JOIN wms_inventory wms
    ON erp.product_id = wms.product_id
    AND wms.warehouse_code = 'MAIN';

6. Performance-Falle: Die Basistabellen werden doppelt gelesen

Ein struktureller Nachteil jeder UNION-basierten Emulation besteht darin, dass beide Basistabellen effektiv zweimal gescannt oder zweimal über den Index gelesen werden, einmal im LEFT-JOIN-Teil und einmal im RIGHT-JOIN- oder NOT-EXISTS-Teil. Bei kleinen bis mittleren Tabellen fällt dieser zusätzliche Aufwand kaum ins Gewicht, bei sehr großen Faktentabellen im zweistelligen Millionenbereich kann er jedoch spürbar zur Gesamtlaufzeit beitragen, insbesondere wenn zusätzlich komplexe Filterbedingungen oder Aggregationen auf dem Ergebnis der Emulation aufbauen.

In solchen Fällen lohnt sich die Prüfung, ob eine Umformulierung des ursprünglichen Anwendungsfalls vermeidbar ist, etwa indem der Datenabgleich in zwei separate, unabhängig ausführbare und gegebenenfalls parallel laufende Abfragen aufgeteilt wird, statt zwingend ein einziges kombiniertes Ergebnis in einer Query zu verlangen. Wo eine einzelne Ergebnismenge tatsächlich nötig ist, hilft ein genauer Blick auf die verwendeten Indizes, da beide Teile der UNION von denselben Indizes auf der Join-Spalte profitieren sollten.

7. UNION versus UNION ALL: wann welche Variante korrekt ist

Bei der klassischen Emulation mit zwei vollständigen Joins, LEFT JOIN und RIGHT JOIN, ist UNION zwingend erforderlich, weil beide Teilabfragen dieselben, tatsächlich übereinstimmenden Zeilen jeweils vollständig zurückliefern und diese Duplikate ohne Duplikatentfernung im Endergebnis doppelt erscheinen würden. Ein versehentliches UNION ALL an dieser Stelle ist einer der häufigsten Fehler bei der Emulation und führt zu einem stillen, aber fachlich falschen Ergebnis mit doppelten Zeilen.

Bei der Variante mit NOT EXISTS im zweiten Teil ist UNION ALL dagegen nicht nur erlaubt, sondern die bessere Wahl, weil die NOT-EXISTS-Bedingung bereits sicherstellt, dass keine Überschneidung zwischen beiden Teilergebnissen entstehen kann. UNION ALL vermeidet in diesem Fall den unnötigen Duplikaterkennungsschritt und ist damit in aller Regel die performantere der beiden vorgestellten Emulationsvarianten.

8. Spaltenreihenfolge und Datentyp-Kompatibilität nicht vergessen

Wie bei jeder UNION-Operation müssen beide Teilabfragen exakt dieselbe Anzahl an Spalten in derselben Reihenfolge mit kompatiblen Datentypen liefern. Ein häufiger Fehler bei der FULL-JOIN-Emulation entsteht, wenn im zweiten Teil der UNION die Spaltenreihenfolge versehentlich vertauscht wird, weil die Tabellenreihenfolge im FROM-Teil gegenüber dem ersten Teil gedreht wurde. Das Ergebnis bleibt syntaktisch gültig, liefert aber fachlich falsch zugeordnete Werte in den falschen Spalten.

Ein bewährter Schutz gegen diesen Fehler ist, die Spaltenliste in beiden Teilen der UNION explizit mit denselben Alias-Namen in derselben Reihenfolge zu schreiben, statt sich auf die implizite Positionszuordnung zu verlassen. Ein anschließender Testlauf mit gezielt konstruierten Testdaten, die sowohl Übereinstimmungen als auch Zeilen nur auf jeweils einer Seite enthalten, deckt Vertauschungsfehler dieser Art zuverlässig auf, bevor sie in eine produktive Auswertung gelangen.

9. Mit COALESCE eine vereinheitlichte Schlüsselspalte bilden

Sowohl beim nativen FULL OUTER JOIN als auch bei jeder Emulation entsteht dasselbe praktische Folgeproblem: Das Ergebnis enthält zwei getrennte Schlüsselspalten, eine aus der linken und eine aus der rechten Tabelle, wobei je nach Zeile jeweils genau eine der beiden NULL sein kann. Für eine nachgelagerte Verarbeitung, etwa eine weitere GROUP-BY-Aggregation oder eine Anzeige in einem Abgleichs-Report, ist eine einzelne, durchgehend gefüllte Schlüsselspalte deutlich praktischer als zwei parallele, teilweise leere Spalten.

Die Funktion COALESCE liefert genau dafür die einfachste Lösung: Sie gibt den ersten nicht-NULL-Wert aus einer Liste von Argumenten zurück, sodass COALESCE aus der linken und der rechten Schlüsselspalte in jeder Zeile zuverlässig den tatsächlich vorhandenen Wert liefert, unabhängig davon, aus welcher der beiden Ursprungstabellen die Zeile stammt. Diese vereinheitlichte Spalte eignet sich anschließend problemlos als GROUP-BY-Schlüssel oder als Anzeigespalte, ohne dass die aufrufende Anwendung wissen muss, welche der beiden Quelltabellen den jeweiligen Datensatz ursprünglich geliefert hat.


SELECT
    COALESCE(erp.product_id, wms.product_id) AS unified_product_id,
    erp.stock_quantity AS erp_quantity,
    wms.stock_quantity AS wms_quantity,
    CASE
        WHEN erp.product_id IS NULL THEN 'nur im WMS'
        WHEN wms.product_id IS NULL THEN 'nur im ERP'
        ELSE 'in beiden Systemen'
    END AS abgleich_status
FROM erp_inventory erp
LEFT JOIN wms_inventory wms ON erp.product_id = wms.product_id

UNION ALL

SELECT
    COALESCE(erp.product_id, wms.product_id),
    erp.stock_quantity,
    wms.stock_quantity,
    'nur im WMS'
FROM wms_inventory wms
LEFT JOIN erp_inventory erp ON erp.product_id = wms.product_id
WHERE erp.product_id IS NULL;
Ansatz Benötigte Join-Typen Duplikat-Handling Relative Performance
Nativer FULL OUTER JOIN FULL OUTER JOIN keine Duplikate möglich am effizientesten, wo verfügbar
LEFT JOIN + RIGHT JOIN + UNION LEFT JOIN, RIGHT JOIN UNION zwingend nötig doppelter Tabellenscan, Duplikatentfernung
LEFT JOIN + NOT EXISTS + UNION ALL LEFT JOIN, NOT EXISTS UNION ALL ausreichend doppelter Scan, aber ohne Duplikaterkennung
Nur INNER JOIN (falsch) INNER JOIN irrelevant schnell, aber fachlich falsch

Mironsoft

Datenbank-Optimierung, Query-Tuning und Migrationen

SQL-Abfragen, die bei Wachstum immer langsamer werden?

Wir analysieren und optimieren SQL-Datenbanken unabhängig vom eingesetzten System, planen sichere Migrationen und Schema-Änderungen und bringen Teams Query-Optimierung praxisnah bei.

Query-Optimierung

Langsame Abfragen analysieren und mit Indizes und Explain-Plänen gezielt beschleunigen.

Migrations-Planung

Schema-Änderungen und Datenmigrationen sicher und ohne Downtime umsetzen.

Team-Schulung

SQL-Grundlagen und Performance-Denken praxisnah im Entwicklerteam verankern.

10. Zusammenfassung

FULL OUTER JOIN emulieren: Das Wichtigste auf einen Blick

Grundprinzip

LEFT JOIN und RIGHT JOIN werden per UNION zusammengeführt, um Zeilen aus beiden Tabellen unabhängig von Übereinstimmungen zu erhalten.

MySQL-Praxisrelevanz

MySQL bot lange keine native FULL-JOIN-Syntax, die Emulation gehört dort zum Standardwerkzeug für Datenabgleiche.

WHERE-Falle vermeiden

Fachliche Filterbedingungen gehören in die ON-Klausel, sonst werden NULL-Zeilen versehentlich aus dem Ergebnis gefiltert.

UNION ALL wo möglich

Die NOT-EXISTS-Variante erlaubt UNION ALL statt UNION und spart dadurch den Duplikaterkennungsschritt.

11. FAQ: FULL OUTER JOIN emulieren: Das Wichtigste auf einen Blick

1Warum bietet MySQL keinen nativen FULL OUTER JOIN?
MySQL hat diese Funktion historisch nie implementiert, obwohl sie Teil des SQL-Standards ist. Entwickler müssen sie über eine Kombination aus LEFT JOIN, RIGHT JOIN oder NOT EXISTS und UNION selbst nachbilden.
2Warum ist UNION statt UNION ALL bei der klassischen Emulation zwingend?
Weil sich übereinstimmende Zeilen sowohl im LEFT-JOIN-Teil als auch im RIGHT-JOIN-Teil identisch wiederfinden. UNION entfernt diese Duplikate automatisch, UNION ALL würde sie doppelt im Ergebnis belassen.
3Wann darf ich UNION ALL statt UNION bei der Emulation verwenden?
Wenn der zweite Teil der UNION über eine NOT-EXISTS-Bedingung von vornherein ausschließlich nicht übereinstimmende Zeilen liefert, kann keine Überschneidung entstehen und UNION ALL ist sicher sowie performanter.
4Was passiert, wenn ich eine WHERE-Bedingung statt einer ON-Bedingung verwende?
Eine WHERE-Bedingung auf eine Spalte der rechten Tabelle filtert versehentlich genau die Zeilen heraus, die als NULL-Zeilen erhalten bleiben sollten, weil WHERE erst nach dem Join ausgewertet wird.
5Wie stark verschlechtert die Emulation die Performance gegenüber einem nativen FULL JOIN?
Beide Basistabellen werden effektiv zweimal gelesen, was bei kleinen Tabellen kaum ins Gewicht fällt, bei sehr großen Tabellen im zweistelligen Millionenbereich aber spürbar zur Laufzeit beitragen kann.
6Muss ich bei der Emulation auf die Spaltenreihenfolge achten?
Ja, beide Teile der UNION müssen exakt dieselbe Spaltenanzahl in derselben Reihenfolge mit kompatiblen Datentypen liefern, sonst werden Werte in fachlich falsche Spalten einsortiert.
7Funktioniert die Emulation auch mit mehr als zwei Tabellen?
Grundsätzlich ja, allerdings wird die Formulierung mit jeder weiteren Tabelle deutlich komplexer und die Anzahl der nötigen UNION-Teile wächst schnell, sodass eine sorgfältige Strukturierung wichtig wird.
8Ist die NOT-EXISTS-Variante immer die bessere Wahl?
In den meisten Fällen ja, weil sie UNION ALL statt UNION erlaubt und dadurch den zusätzlichen Duplikaterkennungsschritt einspart. Bei sehr einfachen Fällen mit kleinen Tabellen ist der Unterschied allerdings selten messbar.
9Kann ich die Emulation mit zusätzlichen Aggregationen kombinieren?
Ja, das emulierte Ergebnis lässt sich wie jede andere Tabelle als Subquery oder CTE weiterverwenden und anschließend gruppieren oder aggregieren, ohne dass sich die Emulationslogik selbst ändert.
10Seit wann bietet MySQL überhaupt eine Alternative zu dieser Emulation?
MySQL bietet bis heute keinen nativen FULL-OUTER-JOIN-Operator wie PostgreSQL, SQL Server oder Oracle. Die UNION-basierte Emulation bleibt dort die einzige zuverlässige Standardlösung.