Eine Tabelle hat „leere“ Werte, eine Abfrage filtert darauf – und plötzlich fehlen Datensätze oder tauchen unerwartet auf. In vielen Fällen steckt dahinter kein Bug im Datenbank-System, sondern ein Missverständnis rund um SQL NULL. NULL bedeutet nicht „leer“ und nicht „0“, sondern „unbekannt“ (kein Wert vorhanden oder nicht bestimmbar). Genau deshalb verhält sich NULL in Vergleichen, JOINs und Aggregationen anders als normale Werte.
Wer diese Regeln einmal sauber verinnerlicht, spart sich viele Stunden Debugging und baut Abfragen, die auch bei unvollständigen Daten stabil bleiben.
Was NULL in SQL wirklich bedeutet
NULL ist „unbekannt“ – nicht leer und nicht falsch
NULL steht in SQL für einen fehlenden oder unbekannten Wert. Das kann unterschiedliche Ursachen haben: Ein Feld wurde nie befüllt, eine Information ist noch nicht vorhanden oder sie ist in der Realität nicht anwendbar (z. B. „Zweitname“). Wichtig ist: NULL ist kein „leerer String“ („“), keine 0 und kein Platzhalter.
Ein hilfreiches Bild: Wenn ein Wert unbekannt ist, kann eine Datenbank nicht sicher sagen, ob „unbekannt = 5“ wahr oder falsch ist. Das Ergebnis ist dann weder wahr noch falsch, sondern „unbekannt“.
Dreiwertige Logik: TRUE, FALSE und UNKNOWN
SQL arbeitet bei Bedingungen mit einer dreiwertigen Logik. Neben TRUE und FALSE gibt es UNKNOWN. Dieser dritte Zustand entsteht typischerweise, sobald NULL beteiligt ist. Das ist entscheidend, weil WHERE-Filter nur Zeilen behalten, deren Bedingung am Ende TRUE ist. FALSE und UNKNOWN werden beide herausgefiltert.
Warum „= NULL“ nie funktioniert (und was stattdessen gilt)
Vergleiche mit NULL liefern nicht TRUE
Ein klassischer Fehler ist so etwas wie NULL vergleichen mit einem normalen Operator:
Beispielgedanke: „Zeige alle Nutzer, deren Telefon = NULL ist.“
In SQL ist das jedoch falsch, weil der Ausdruck telefon = NULL nicht TRUE werden kann. Er wird zu UNKNOWN, weil nicht entschieden werden kann, ob „unbekannt gleich unbekannt“ ist. Daher liefert ein WHERE-Filter damit keine Treffer.
Richtig prüfen: IS NULL und IS NOT NULL
Für NULL gibt es eigene Operatoren:
- IS NULL findet Zeilen, in denen kein Wert steht.
- IS NOT NULL findet Zeilen, in denen ein Wert vorhanden ist.
Diese Prüfungen sind eindeutig und liefern TRUE oder FALSE.
Typischer Stolperstein: NOT (Spaltenwert = irgendwas)
Auch „nicht gleich“ kann überraschen. Wenn eine Spalte NULL ist, ist sowohl „spalte = ‚A’“ als auch „spalte <> ‚A’“ UNKNOWN. Damit fallen NULL-Zeilen oft unbemerkt durchs Raster. Wer NULL-Zeilen bewusst einschließen oder ausschließen will, muss das explizit formulieren (z. B. „spalte <> ‚A‘ OR spalte IS NULL“ – je nach gewünschter Logik).
JOINs und NULL: Warum Datensätze „verschwinden“
NULL in Join-Spalten matcht nicht
JOINs basieren auf Vergleichsbedingungen. Wenn eine Join-Spalte NULL enthält, kann kein Match entstehen, weil „NULL = NULL“ nicht TRUE wird. Das führt dazu, dass bei INNER JOIN Zeilen fehlen können, obwohl „auf beiden Seiten nichts steht“.
LEFT JOIN: vorhanden, aber mit leeren Spalten auf der rechten Seite
Ein LEFT JOIN behält alle Zeilen der linken Tabelle. Wenn rechts kein Match entsteht (auch wegen NULL), werden die Spalten der rechten Tabelle als NULL geliefert. Das ist korrekt, wirkt aber schnell wie „Daten fehlen“. Besonders kritisch wird es, wenn danach Bedingungen auf Spalten der rechten Tabelle in der WHERE-Klausel stehen: Damit wird der LEFT JOIN oft unbeabsichtigt wieder zum INNER JOIN, weil WHERE NULL-Zeilen herausfiltert.
Praxis-Tipp: Bedingungen, die zur Join-Logik gehören, gehören häufig in die ON-Klausel. Dann bleibt der LEFT JOIN-Charakter erhalten.
Alternative Muster: EXISTS statt JOIN
Wenn es nur darum geht zu prüfen, ob passende Datensätze existieren, ist EXISTS oft robuster als ein JOIN. EXISTS bewertet „gibt es mindestens eine passende Zeile?“ und reduziert das Risiko, durch Mehrfachtreffer oder NULL-Effekte versehentlich Duplikate oder Lücken zu erzeugen.
Aggregationen: COUNT, SUM und Co. verhalten sich unterschiedlich
COUNT(*) zählt Zeilen, COUNT(spalte) zählt Nicht-NULL
Aggregationen sind ein weiterer Ort, an dem NULL oft falsch interpretiert wird:
- COUNT(*) zählt alle Zeilen – unabhängig von NULL in einzelnen Spalten.
- COUNT(spalte) zählt nur Zeilen, in denen spalte nicht NULL ist.
Das ist nützlich, kann aber Statistiken verfälschen, wenn im Kopf „Zeilenanzahl“ und „befüllte Werte“ nicht getrennt werden.
SUM/AVG ignorieren NULL (meist) – aber das ist nicht immer gewünscht
SUM(spalte) und AVG(spalte) ignorieren NULL-Werte typischerweise. Das bedeutet: Fehlende Werte werden nicht als 0 behandelt, sondern schlicht ausgelassen. Ob das korrekt ist, hängt vom fachlichen Kontext ab. Bei „Umsatz unbekannt“ ist Ignorieren sinnvoll; bei „Anzahl verkaufter Artikel“ könnte NULL fachlich eher „0“ bedeuten – dann sollte beim Schreiben der Daten oder in der Abfrage eine klare Entscheidung getroffen werden.
NULL sauber behandeln: COALESCE, Default-Werte und Datenmodell
COALESCE: Ersatzwert für Anzeige und Berechnung
Mit COALESCE lässt sich in Abfragen ein Ersatzwert wählen, falls eine Spalte NULL ist. Das ist hilfreich für Ausgaben („zeige ‚—‘ statt NULL“) oder für Berechnungen („nimm 0, wenn kein Wert vorhanden ist“). Wichtig ist, Ersatzwerte bewusst zu wählen: Ein Display-Platzhalter gehört meist nur in die Anzeige, nicht in die Speicherung.
Default-Werte sind kein Ersatz für sauberes Denken
Ein Default (Standardwert) kann sinnvoll sein, wenn ein Feld fachlich immer einen Wert haben sollte (z. B. Status = ’new‘). Er ist aber problematisch, wenn er „unbekannt“ verschleiert. Eine 0 kann eben „keine Menge“ bedeuten – oder „nicht erfasst“. Wenn beides möglich ist, ist NULL oft die ehrlichere Modellierung, kombiniert mit klaren Regeln, wann ein Wert gesetzt werden muss.
NOT NULL + Constraints: Qualität in die Daten bringen
Viele NULL-Probleme entstehen nicht durch SQL, sondern durch unklare Datenregeln. Wenn ein Feld wirklich Pflicht ist, sollte es als NOT NULL modelliert werden. Ergänzend helfen Constraints (Regeln auf Datenbank-Ebene), damit ungültige Zustände gar nicht erst gespeichert werden. Passend dazu erklärt SQL Constraints verstehen – saubere Daten ohne Überraschungen, wie solche Regeln in der Praxis aussehen.
Praktische Schritte, um NULL-Bugs schnell zu finden
Wenn eine Abfrage „komische“ Ergebnisse liefert, hilft eine kurze Routine. Diese Schritte sind bewusst pragmatisch gehalten und funktionieren in fast jedem Projekt:
- In der WHERE-Klausel gezielt nach NULL prüfen: erst IS NULL/IS NOT NULL testen, dann Filter kombinieren.
- JOINs gegenchecken: Sind Join-Spalten auf beiden Seiten wirklich befüllt? Gibt es NULL in Keys oder Fremdschlüsseln?
- LEFT JOIN prüfen: Stehen Bedingungen auf rechten Tabellenfeldern in WHERE statt ON?
- Aggregationen vergleichen: COUNT(*) vs. COUNT(spalte) nebeneinander ausgeben, um fehlende Werte sichtbar zu machen.
- Wenn Ersatzwerte verwendet werden: COALESCE nur dort einsetzen, wo „Ersatz“ fachlich wirklich gemeint ist.
Kleine Vergleichsbox: NULL, leerer String und 0
Gerade in Webprojekten (Formulare, Imports, CSV) werden diese drei Dinge häufig durcheinandergebracht. Die Unterschiede sollten im Team klar sein:
| Wert | Bedeutung | Typische Quelle | Gefahr |
|---|---|---|---|
| NULL | Unbekannt / nicht vorhanden | nicht ausgefülltes Feld, fehlende Info | Vergleiche liefern UNKNOWN, Filter greifen anders |
| „“ (leer) | Wert ist vorhanden, aber leer | HTML-Formular sendet leeren String | „leer“ wird wie normaler String behandelt, kann NULL-Checks umgehen |
| 0 | Konkreter Zahlenwert | Default, Berechnung, Import | Verschleiert „unbekannt“, wenn 0 nur Platzhalter ist |
Häufige Fragen aus der Praxis
Warum filtert WHERE spalte <> ‚x‘ NULL-Zeilen weg?
Weil der Ausdruck bei NULL nicht TRUE werden kann, sondern UNKNOWN. WHERE behält nur TRUE. Wenn NULL-Zeilen enthalten sein sollen, muss das extra formuliert werden, etwa durch „… OR spalte IS NULL“.
Ist es besser, NULL zu vermeiden?
Nicht grundsätzlich. NULL ist sinnvoll, wenn ein Wert fachlich fehlen kann oder noch nicht bekannt ist. Wichtig ist, im Datenmodell klar zu definieren, welche Felder Pflicht sind (NOT NULL) und welche bewusst NULL sein dürfen. Das reduziert Logikfehler in Abfragen deutlich.
Wie passt das zu Input-Validierung im Backend?
Viele NULL-Probleme entstehen beim Speichern: Ein leeres Formularfeld wird als „“ gespeichert, obwohl NULL gemeint war (oder umgekehrt). Eine saubere Validierung und Normalisierung (z. B. leere Strings zu NULL) hilft. Ergänzend ist Input-Validierung im Backend hilfreich, um Eingaben einheitlich zu behandeln.
Wie lässt sich NULL bei APIs sauber transportieren?
In JSON ist null ein eigener Wert, aber Frontends senden oft leere Strings. Wichtig ist eine klare API-Regel: Was bedeutet null? Was bedeutet „“? Bei komplexen Datenformaten hilft zudem eine Schema-Validierung. Dazu passt JSON Schema verstehen – Datenvalidierung ohne Bauchgefühl, um Erwartungen an Felder explizit zu machen.
Entscheidungshilfe: NULL oder Pflichtfeld?
- Kann der Wert fachlich fehlen?
- Ja: NULL zulassen und in Abfragen IS NULL/IS NOT NULL bewusst verwenden.
- Nein: Feld als NOT NULL modellieren und beim Schreiben validieren.
- Ist „0“ oder „“ ein echter, bedeutungsvoller Zustand?
- Ja: als konkreten Wert speichern und separat von NULL behandeln.
- Nein: lieber NULL speichern, statt Platzhalterwerte zu verwenden.
- Muss eine Berechnung fehlende Werte als 0 behandeln?
- Ja: in der Berechnung bewusst Ersatz wählen (z. B. mit COALESCE).
- Nein: NULL ignorieren lassen und Ergebnisse entsprechend interpretieren.

