SQL EXISTS: Zeilen nach verknüpften Datensätzen filtern

SQL EXISTS gibt true zurück, wenn eine korrelierte Unterabfrage mindestens eine passende Zeile findet. Damit filterst du übergeordnete Zeilen nach vorhandenen (oder fehlenden) verknüpften Detailzeilen, ohne Ergebnisse so zu duplizieren, wie es ein JOIN kann. Query-Planer führen EXISTS häufig als Semi-Join aus, daher können sie die Unterabfrage nach dem ersten Treffer beenden.

SQL EXISTS Beispiel zum Filtern nach vorhandenen Zeilen

Ausgabe:

Die Ausgabe erscheint hier...

Ausgabe:

+------+
| name |
+------+
| Alma |
| Cara |
+------+
2 row(s)

Warum EXISTS Alma und Cara zurückgibt

  1. Es werden zwei Tabellen erstellt: kunden enthält drei Personen, bestellungen enthält drei Bestellungen, verknüpft über kunden_id.
  2. WHERE EXISTS (SELECT 1 FROM bestellungen b WHERE b.kunden_id = k.id) führt die Unterabfrage einmal pro Kundenzeile aus. Gibt es mindestens eine passende Bestellung, passiert der Kunde den Filter.
  3. SELECT 1 in der Unterabfrage ist Konvention. Die Datenbank ignoriert die Select-Liste und prüft nur, ob irgendeine Zeile passt.
  4. Ben hat keine Bestellungen, daher liefert seine Unterabfrage keine Zeilen und EXISTS wird false. Alma und Cara haben jeweils mindestens eine Bestellung und erscheinen im Ergebnis.

Im Gegensatz zu einem INNER JOIN dupliziert EXISTS die äußere Zeile nie. Alma hat zwei Bestellungen und erscheint trotzdem nur einmal, weil EXISTS nur fragt: “Existiert ein Treffer?” und nicht “Wie viele?”.

NOT EXISTS vs NOT IN: Die NULL-Falle

SQL NOT EXISTS (geschrieben als NOT EXISTS) ist die sichere Wahl für Anti-Joins auf NULL-fähigen Spalten. Gibt eine Unterabfrage NULL-Werte zurück, produziert NOT IN unerwartete Ergebnisse, weil jeder Vergleich mit NULL den Wert unbekannt ergibt:

-- Angenommen, bestellungen enthält eine Zeile mit NULL in kunden_id
-- NOT IN verwirft stillschweigend ALLE Zeilen
SELECT name FROM kunden
WHERE id NOT IN (SELECT kunden_id FROM bestellungen);

-- NOT EXISTS funktioniert weiterhin korrekt
SELECT name FROM kunden k
WHERE NOT EXISTS (
  SELECT 1 FROM bestellungen b WHERE b.kunden_id = k.id
);

NOT IN behandelt id NOT IN (1, 3, NULL) wie id <> 1 AND id <> 3 AND id <> NULL. Der letzte Vergleich ist immer unbekannt, dadurch wird das gesamte Prädikat nie true und es kommen keine Zeilen zurück. NOT EXISTS vermeidet das, weil es Zeilenexistenz prüft und nicht Wertgleichheit.

Häufige Fehler bei SQL EXISTS

Fehlendes Korrelationsprädikat

Ist die Unterabfrage nicht zur äußeren Zeile korreliert, wird EXISTS zu einem konstanten Test: Sobald irgendeine Bestellung existiert, passt jeder Kunde.

-- Falsch: keine Korrelation zur äußeren Zeile
SELECT k.name FROM kunden k
WHERE EXISTS (SELECT 1 FROM bestellungen);

-- Richtig: über kunden_id korreliert
SELECT k.name FROM kunden k
WHERE EXISTS (SELECT 1 FROM bestellungen b WHERE b.kunden_id = k.id);

JOIN verwenden, obwohl nur Existenz zählt

Ein INNER JOIN liefert eine Zeile pro Treffer. Kunden mit mehreren Bestellungen erscheinen deshalb mehrfach.

SELECT k.name
FROM kunden k
JOIN bestellungen b ON b.kunden_id = k.id;

Verwende EXISTS, solange du keine Spalten aus bestellungen benötigst.

EXISTS vs IN vs JOIN

AnsatzVerwenden, wenn …
EXISTS (subquery)Die Existenz verknüpfter Zeilen geprüft wird; die Unterabfrage ist korreliert
NOT EXISTS (subquery)Anti-Join, besonders bei NULL-fähigen Fremdschlüsseln
IN (subquery)Gegen eine kleine, nicht NULL-fähige Wertemenge abgeglichen wird
INNER JOINDu Spalten aus beiden Tabellen im Ergebnis brauchst
LEFT JOIN ... IS NULLAlternative für Anti-Joins; wird von Query-Planern teils bevorzugt

Regel: Verwende EXISTS für Fragen vom Typ “Existiert eine verknüpfte Zeile?”. Wechsle zu JOIN, wenn du Daten von beiden Seiten brauchst.

Performance-Hinweise

EXISTS wird oft als Semi-Join geplant. Ein Index auf der Korrelationsspalte (bestellungen.kunden_id hier) erlaubt der Engine, pro Kunde nach der ersten passenden Zeile zu suchen, statt bestellungen voll zu scannen. NOT EXISTS profitiert vom gleichen Index. Ist EXISTS langsam, prüfe den Query-Plan und ob die Korrelationsspalte indexiert ist.

FAQ

Stoppt EXISTS nach dem ersten Treffer?

Logisch wird das Ergebnis true, sobald eine passende Zeile existiert. Viele Datenbanken setzen das als Semi-Join mit frühem Abbruch um, die genaue Strategie entscheidet aber der Query-Planer. Schau in den Ausführungsplan, wenn du das Verhalten deiner Datenbank bestätigen musst.

Kann EXISTS in UPDATE und DELETE verwendet werden?

Ja. UPDATE ... WHERE EXISTS (subquery) und DELETE ... WHERE EXISTS (subquery) schränken ein, welche Zeilen anhand verknüpfter Daten geändert oder gelöscht werden. Behalte dasselbe Korrelationsprädikat wie bei SELECT bei, damit jede Zielzeile nur ihre eigenen verknüpften Zeilen prüft.

Was ist der Unterschied zwischen EXISTS und IN in SQL?

EXISTS prüft, ob eine (meist korrelierte) Unterabfrage irgendeine Zeile zurückgibt. IN vergleicht einen Wert mit einer Ergebnismenge. EXISTS funktioniert natürlich mit der Korrelation zur äußeren Abfrage, und viele Engines können nach dem ersten Treffer stoppen; IN passt gut zu kleinen Literallisten oder unkorrelierten Membership-Prüfungen. Für Anti-Joins auf NULL-fähigen Spalten ist NOT EXISTS gegenüber NOT IN vorzuziehen.