INTERSECT und EXCEPT: Mengenoperationen praktisch einsetzen
AI generated
SELECT
JOIN
SQL / Set-Operatoren
INTERSECT und EXCEPT: Mengenoperationen praktisch einsetzen
Wann Set-Operatoren lesbarer sind als äquivalente JOIN- oder NOT-EXISTS-Konstruktionen

UNION und UNION ALL gehören zum Standardrepertoire, aber die beiden anderen Mengenoperatoren des SQL-Standards, INTERSECT und EXCEPT, fristen in vielen Codebasen ein Schattendasein, obwohl sie für bestimmte Fragestellungen die klarste und wartungsfreundlichste Lösung darstellen. INTERSECT beantwortet, welche Zeilen in zwei Ergebnismengen gleichzeitig vorkommen, EXCEPT beantwortet, welche Zeilen in der ersten Menge vorkommen, aber nicht in der zweiten. Beide Fragen lassen sich zwar auch mit JOIN- oder NOT-EXISTS-Konstruktionen lösen, doch der Set-Operator macht die eigentliche fachliche Absicht oft auf den ersten Blick sichtbar. Dieser Artikel zeigt, wann sich der Umstieg lohnt, wie sich die Syntax zwischen Datenbanken unterscheidet und wie die Performance im Vergleich zu Alternativen tatsächlich ausfällt.

10 Min. Lesezeit INTERSECT EXCEPT

1. Grundprinzip: Mengenvergleich statt Zeilenverknüpfung

Ein JOIN verknüpft Zeilen aus zwei Tabellen anhand eines Vergleichsschlüssels und liefert eine kombinierte Zeile aus beiden Quellen. INTERSECT und EXCEPT arbeiten grundlegend anders: Beide vergleichen zwei vollständige Ergebnismengen mit identischer Spaltenstruktur miteinander und liefern als Ergebnis wieder eine Menge derselben Struktur, keine kombinierte, sondern eine gefilterte Zeile. Das macht sie zur natürlichen Wahl für Fragestellungen, die tatsächlich mengentheoretischer Natur sind, etwa welche Kunden sowohl in diesem als auch im letzten Quartal bestellt haben, oder welche Produkte im Lager geführt, aber noch nie verkauft wurden.

Technisch verhalten sich beide Operatoren wie UNION: Sie verlangen dieselbe Anzahl an Spalten mit kompatiblen Datentypen in beiden Teilabfragen, und beide entfernen per Voreinstellung Duplikate im Ergebnis, ähnlich wie UNION ohne ALL. Einige Datenbanken bieten inzwischen auch INTERSECT ALL und EXCEPT ALL an, die Duplikate unter Berücksichtigung ihrer Häufigkeit behandeln, statt sie vollständig zu entfernen, was für bestimmte Bestandsvergleiche relevant sein kann.

2. INTERSECT in der Praxis: Kunden mit Bestellungen in zwei Zeiträumen

Ein klassisches Anwendungsbeispiel für INTERSECT ist die Suche nach Kunden, die in zwei unterschiedlichen Zeiträumen jeweils mindestens eine Bestellung aufgegeben haben, etwa um wiederkehrende Käufer von Einmalkäufern zu unterscheiden. Statt eine Abfrage mit doppeltem Self-Join und passenden Datumsfiltern zu konstruieren, liest sich die INTERSECT-Variante nahezu wie die ursprüngliche fachliche Frage.

Der entscheidende Lesbarkeitsvorteil zeigt sich, sobald mehr als zwei Bedingungen kombiniert werden sollen. Eine Kette aus drei oder vier INTERSECT-Verknüpfungen bleibt für jede weitere Bedingung um genau einen zusätzlichen SELECT-Block erweiterbar, während die äquivalente JOIN-Lösung mit jeder weiteren Bedingung eine zusätzliche Verknüpfung und zusätzliche NULL-Behandlung erfordert, was die Abfrage schnell unübersichtlich macht.


-- Kunden, die sowohl in Q1 als auch in Q2 bestellt haben
SELECT customer_id FROM orders
WHERE order_date BETWEEN '2026-01-01' AND '2026-03-31'
INTERSECT
SELECT customer_id FROM orders
WHERE order_date BETWEEN '2026-04-01' AND '2026-06-30';

3. EXCEPT in der Praxis: Produkte ohne Verkäufe

EXCEPT liefert alle Zeilen der ersten Abfrage, die nicht in der zweiten Abfrage vorkommen, und eignet sich damit hervorragend für typische Differenzfragen wie welche Produkte sind im Lagerbestand geführt, wurden aber im laufenden Jahr kein einziges Mal verkauft. Die semantische Nähe zur fachlichen Frage macht den Code für Kolleginnen und Kollegen ohne tiefes SQL-Wissen deutlich zugänglicher als eine verschachtelte NOT-EXISTS-Konstruktion mit korrelierter Subquery.

Wichtig ist dabei, dass EXCEPT wie INTERSECT auf der vollständigen Zeile basiert, nicht nur auf einer einzelnen Schlüsselspalte. Sollen nur bestimmte Spalten für den Vergleich herangezogen werden, müssen beide SELECT-Listen exakt auf diese Spalten reduziert werden, sonst führen bereits kleine Unterschiede in Begleitspalten wie einem Zeitstempel dazu, dass eine eigentlich gleiche Zeile fälschlich als unterschiedlich gewertet wird.


-- Produkte im Bestand ohne Verkauf im laufenden Jahr
SELECT product_id FROM inventory
WHERE quantity_on_hand > 0
EXCEPT
SELECT product_id FROM order_items
WHERE order_date >= '2026-01-01';

4. Die äquivalente JOIN- und NOT-EXISTS-Variante im Vergleich

Dieselbe INTERSECT-Abfrage lässt sich auch mit einem INNER JOIN auf die Kunden-ID formulieren, wobei zusätzlich DISTINCT benötigt wird, um Duplikate aus mehreren Bestellungen im selben Zeitraum zu entfernen. Diese Variante ist funktional gleichwertig, verlangt aber vom Leser, gedanklich nachzuvollziehen, dass eine reine Existenzprüfung gemeint ist, obwohl syntaktisch eine Verknüpfung mit allen ihren möglichen Nebeneffekten wie Duplizierung bei Eins-zu-Viele-Beziehungen im Code steht.

Die EXCEPT-Abfrage lässt sich äquivalent mit einer NOT-EXISTS-Subquery formulieren, die für jede Zeile der äußeren Abfrage prüft, ob eine passende Zeile in der zweiten Menge existiert. NOT EXISTS ist funktional sehr nah an EXCEPT und in vielen Fällen sogar performanter, weil der Optimizer eine korrelierte Subquery oft als Anti-Join ausführt, aber der Code ist bei mehreren verketteten Bedingungen deutlich schwerer zu lesen als eine Kette aus mehreren EXCEPT-Blöcken.


-- Äquivalent zu EXCEPT, funktional identisch
SELECT i.product_id
FROM inventory i
WHERE i.quantity_on_hand > 0
  AND NOT EXISTS (
      SELECT 1 FROM order_items oi
      WHERE oi.product_id = i.product_id
        AND oi.order_date >= '2026-01-01'
  );

5. Performance-Vergleich: Set-Operator vs. JOIN vs. NOT EXISTS

In modernen Optimizer-Implementierungen von PostgreSQL, SQL Server und Oracle werden INTERSECT und EXCEPT intern häufig auf denselben Ausführungsplan wie eine semantisch äquivalente NOT-EXISTS- oder Semi-Join-Konstruktion abgebildet, insbesondere wenn beide Teilabfragen auf indizierten Spalten basieren. In der Praxis zeigt ein Blick in EXPLAIN ANALYZE oft, dass sich die Laufzeit der drei Varianten bei gut indizierten Tabellen kaum unterscheidet, weil der Optimizer die eigentliche Absicht hinter allen drei Formulierungen korrekt erkennt.

Ein relevanter Unterschied bleibt dennoch bestehen: INTERSECT und EXCEPT erzwingen implizit eine Duplikatentfernung über die gesamte Zeile, was bei sehr breiten Ergebnismengen einen zusätzlichen Sortier- oder Hash-Schritt bedeuten kann, den eine gezielt geschriebene NOT-EXISTS-Variante ohne DISTINCT nicht benötigt. Bei performance-kritischen Abfragen auf sehr großen Tabellen lohnt sich daher ein expliziter Plan-Vergleich, bevor man sich allein aus Lesbarkeitsgründen für den Set-Operator entscheidet.

6. Oracle-Sonderfall: MINUS statt EXCEPT

Oracle kennt historisch keinen EXCEPT-Operator, sondern verwendet stattdessen das Schlüsselwort MINUS mit identischer Semantik: alle Zeilen der ersten Abfrage, die nicht in der zweiten Abfrage vorkommen. Wer Code portabel zwischen Oracle und anderen Datenbanken halten möchte, muss dieses Detail explizit berücksichtigen, da ein direkt übernommenes EXCEPT in Oracle schlicht mit einem Syntaxfehler abgelehnt wird.

INTERSECT dagegen ist bei Oracle unter demselben Namen wie im SQL-Standard verfügbar und verhält sich identisch zu PostgreSQL, SQL Server und MySQL ab Version 8.0.31. Für datenbank-agnostischen Code empfiehlt sich entweder eine zentrale Abstraktionsschicht, die MINUS und EXCEPT je nach Zieldatenbank austauscht, oder von vornherein der Verzicht auf EXCEPT zugunsten einer NOT-EXISTS-Formulierung, die auf allen relevanten Datenbanken identisch funktioniert.


-- Oracle-Variante von EXCEPT
SELECT product_id FROM inventory
WHERE quantity_on_hand > 0
MINUS
SELECT product_id FROM order_items
WHERE order_date >= DATE '2026-01-01';

7. MySQL-Verfügbarkeit und ältere Versionen

MySQL unterstützt INTERSECT und EXCEPT erst seit Version 8.0.31, deutlich später als die meisten anderen Mengenoperatoren, die schon lange verfügbar waren. In älteren MySQL-Versionen, die in vielen produktiven Umgebungen noch im Einsatz sind, müssen beide Operationen zwingend über JOIN- oder NOT-EXISTS-Konstruktionen nachgebildet werden, es gibt keine native Alternative.

Bei Migrationsprojekten, die Code für ältere und neuere MySQL-Versionen gleichzeitig unterstützen sollen, lohnt sich daher eine bewusste Entscheidung: entweder durchgängig auf die kompatible NOT-EXISTS-Formulierung setzen, oder eine Versionsprüfung vorschalten und je nach Zielumgebung zwischen beiden Varianten wechseln. Für neue Projekte, die ausschließlich auf MySQL 8.0.31 oder neuer, PostgreSQL, SQL Server oder Oracle laufen, spricht dagegen wenig gegen den direkten Einsatz der nativen Operatoren.

8. Wie NULL-Werte in Set-Operatoren behandelt werden

Ein oft übersehener Unterschied zu einer klassischen Gleichheitsprüfung mit dem Operator Gleich betrifft NULL-Werte. Während ein JOIN auf Gleich zwei NULL-Werte niemals als gleich behandelt, weil NULL Gleich NULL in SQL grundsätzlich unbekannt statt wahr ergibt, behandeln INTERSECT und EXCEPT zwei Zeilen mit NULL in derselben Spalte als gleichwertig für den Zweck des Mengenvergleichs. Das entspricht dem Verhalten von DISTINCT, nicht dem Verhalten eines Gleichheitsoperators in einer WHERE-Klausel.

Dieser Unterschied kann zu überraschenden Ergebnissen führen, wenn ein Team einen NOT-EXISTS-Ausdruck durch EXCEPT ersetzt, ohne dieses Detail zu berücksichtigen: Zeilen mit NULL in der Vergleichsspalte, die bei NOT EXISTS aufgrund der Drei-Werte-Logik von SQL erhalten geblieben wären, können bei EXCEPT plummeln, weil sie als gleichwertig zu einer anderen NULL-Zeile in der zweiten Menge gewertet werden. Ein expliziter Test mit realen NULL-Werten in den Testdaten ist bei der Migration zwischen beiden Formulierungen daher unverzichtbar.

9. Entscheidungshilfe: Wann sich der Umstieg lohnt

INTERSECT und EXCEPT lohnen sich besonders dann, wenn die fachliche Frage tatsächlich mengentheoretisch formuliert ist, wenn mehrere ähnliche Bedingungen verkettet werden sollen, oder wenn der Code von Personen gelesen wird, die mit komplexen JOIN-Konstruktionen weniger vertraut sind. Sie eignen sich weniger gut, wenn zusätzliche Spalten aus beiden Quellen im Ergebnis erscheinen sollen, denn dafür ist ein klassischer JOIN grundsätzlich die richtige Wahl, da Set-Operatoren ausschließlich Zeilen aus der Struktur der ersten Teilabfrage zurückgeben.

Als Faustregel gilt: Sobald die Formulierung welche Zeilen kommen in beiden Mengen vor oder welche Zeilen fehlen in der zweiten Menge natürlich klingt, ist ein Set-Operator meist die richtige Wahl. Sobald zusätzliche Informationen aus der zweiten Quelle im Ergebnis benötigt werden, etwa ein Bestelldatum oder ein Produktname aus der verknüpften Tabelle, führt kein Weg an einem klassischen JOIN vorbei.

Operator Bedeutung Oracle-Name MySQL ab Version
INTERSECT Zeilen in beiden Mengen gleichzeitig INTERSECT 8.0.31
EXCEPT Zeilen nur in der ersten Menge MINUS 8.0.31
INTERSECT ALL wie INTERSECT, berücksichtigt Häufigkeit nicht verfügbar nicht verfügbar
EXCEPT ALL wie EXCEPT, berücksichtigt Häufigkeit nicht verfügbar nicht verfügbar
Äquivalent per JOIN INNER JOIN mit DISTINCT identisch seit jeher verfügbar
Äquivalent per NOT EXISTS korrelierte Subquery als Anti-Join identisch seit jeher verfügbar

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

INTERSECT und EXCEPT: Das Wichtigste auf einen Blick

Mengenvergleich statt Verknüpfung

INTERSECT und EXCEPT vergleichen zwei vollständige Ergebnismengen und liefern gefilterte Zeilen statt kombinierter Zeilen wie ein JOIN.

Oracle nutzt MINUS

Wer portablen Code schreibt, muss EXCEPT bei Oracle durch das Schlüsselwort MINUS ersetzen, INTERSECT bleibt unverändert.

Performance meist gleichauf

Bei guter Indizierung wandelt der Optimizer Set-Operatoren, JOINs und NOT-EXISTS-Konstruktionen oft in ähnliche Ausführungspläne um.

NULL-Behandlung wie DISTINCT

Anders als ein Gleichheitsoperator behandeln beide Operatoren zwei NULL-Werte als gleichwertig für den Mengenvergleich.

11. FAQ: INTERSECT und EXCEPT: Das Wichtigste auf einen Blick

1Was ist der grundlegende Unterschied zwischen INTERSECT und einem JOIN?
Ein JOIN kombiniert Spalten aus zwei Tabellen zu einer neuen Zeile, INTERSECT vergleicht zwei vollständige Ergebnismengen mit identischer Struktur und liefert nur Zeilen, die in beiden Mengen vorkommen.
2Entfernt EXCEPT automatisch Duplikate?
Ja, EXCEPT verhält sich wie UNION ohne ALL und entfernt Duplikate aus dem Ergebnis. EXCEPT ALL, sofern von der Datenbank unterstützt, berücksichtigt dagegen die Häufigkeit der Zeilen.
3Warum kennt Oracle kein EXCEPT?
Oracle hat historisch das Schlüsselwort MINUS eingeführt, bevor der SQL-Standard EXCEPT festlegte, und diese Bezeichnung aus Kompatibilitätsgründen beibehalten. Die Semantik beider Schlüsselwörter ist identisch.
4Seit wann unterstützt MySQL INTERSECT und EXCEPT?
Erst seit MySQL 8.0.31. In älteren Versionen müssen beide Operationen über JOIN- oder NOT-EXISTS-Konstruktionen nachgebildet werden.
5Kann ich mit INTERSECT zusätzliche Spalten aus der zweiten Abfrage anzeigen?
Nein, das Ergebnis eines Set-Operators übernimmt ausschließlich die Spaltenstruktur der ersten Teilabfrage. Für zusätzliche Spalten aus der zweiten Quelle ist ein klassischer JOIN die richtige Wahl.
6Sind INTERSECT und NOT EXISTS immer gleich schnell?
Bei guter Indizierung meist ja, da der Optimizer beide oft auf denselben Plan abbildet. Bei sehr breiten Ergebnismengen kann die implizite Duplikatentfernung von INTERSECT jedoch einen zusätzlichen Sortierschritt erfordern.
7Wie werden NULL-Werte bei INTERSECT und EXCEPT behandelt?
Zwei Zeilen mit NULL in derselben Spalte gelten für den Mengenvergleich als gleichwertig, ähnlich wie bei DISTINCT. Das unterscheidet sich vom Verhalten eines Gleichheitsoperators, bei dem NULL Gleich NULL unbekannt statt wahr ergibt.
8Müssen beide Teilabfragen bei INTERSECT exakt dieselben Spaltennamen haben?
Nein, entscheidend ist nur die Anzahl und die Kompatibilität der Datentypen in derselben Reihenfolge. Die Spaltennamen im Ergebnis stammen aus der ersten Teilabfrage.
9Wann ist eine NOT-EXISTS-Formulierung besser als EXCEPT?
Wenn Portabilität zu Oracle oder älteren MySQL-Versionen wichtig ist, oder wenn eine feingranulare Kontrolle über die Vergleichsbedingung nötig ist, die über einen reinen Zeilenvergleich hinausgeht.
10Gibt es INTERSECT ALL und EXCEPT ALL in allen Datenbanken?
Nein, PostgreSQL unterstützt beide Varianten, Oracle und MySQL bislang nicht. Dort muss die Häufigkeitsbetrachtung manuell über zusätzliche Zählspalten nachgebildet werden.