NULL in SQL – warum COUNT(spalte) lügt und wo die leeren Werte landen
NULL ist kein Wert, sondern die Abwesenheit eines Werts. Deshalb zählt COUNT(spalte) anders als COUNT(*), summiert SUM() an leeren Zeilen vorbei und landen beim Sortieren alle Lücken am selben Ende.
NULL bedeutet „unbekannt". Und weil man mit Unbekanntem nicht rechnen kann, verhält sich SQL an genau den Stellen anders, an denen man es zuerst nicht erwartet.
Vergleichen geht nicht, prüfen schon
SELECT * FROM kunden WHERE telefon = NULL; -- liefert IMMER nichts
SELECT * FROM kunden WHERE telefon IS NULL; -- so ist es richtig
SELECT * FROM kunden WHERE telefon IS NOT NULL;NULL = NULL ist nicht wahr, sondern wieder unbekannt. Deshalb gibt es IS NULL. Dasselbe gilt für Rechnungen: preis + rabatt ist NULL, sobald einer der beiden Werte fehlt – die ganze Zeile fällt still aus der Auswertung.
Zählen: COUNT(*) gegen COUNT(spalte)
SELECT
COUNT(*) AS zeilen, -- alle Zeilen
COUNT(telefon) AS mit_telefon, -- nur Zeilen, in denen telefon gefüllt ist
COUNT(*) - COUNT(telefon) AS ohne_telefon
FROM kunden;Das ist kein Sonderfall, sondern die Regel: Jede Aggregatfunktion überspringt NULL. AVG(bewertung) rechnet nur über die abgegebenen Bewertungen – meistens genau richtig, aber man sollte es wissen. Nur COUNT(*) zählt Zeilen statt Werte.
Ersetzen: COALESCE und IFNULL
SELECT
name,
COALESCE(spitzname, name) AS anzeigename, -- erster nicht-leerer Wert
IFNULL(rabatt, 0) AS rabatt, -- SQLite-Kurzform für zwei Argumente
COALESCE(handy, festnetz, 'keine Nummer') AS kontakt
FROM kunden;COALESCE nimmt beliebig viele Argumente und liefert das erste, das nicht NULL ist. Für Summen ist es das Mittel der Wahl:
SELECT kunde_id, SUM(COALESCE(betrag, 0)) AS umsatz FROM posten GROUP BY kunde_id;Sortieren: wo die Lücken landen
-- SQLite sortiert NULL zuerst (aufsteigend). Explizit ans Ende:
SELECT * FROM aufgaben ORDER BY faellig_am IS NULL, faellig_am;
-- Seit SQLite 3.30 auch direkt:
SELECT * FROM aufgaben ORDER BY faellig_am NULLS LAST;Die erste Variante funktioniert überall: faellig_am IS NULL ergibt 0 oder 1, und danach wird zuerst sortiert.
Die Umkehrung, die zwei Werte gleich nennt
-- Findet auch Zeilen, in denen BEIDE Seiten NULL sind:
SELECT * FROM stand WHERE alt IS NOT neu; -- SQLite-SchreibweiseIS und IS NOT vergleichen in SQLite auch NULL sinnvoll – praktisch für Änderungsprüfungen, bei denen <> genau die Zeilen verschluckt, um die es geht.
Fallstrick
Zwei Dinge, die regelmäßig Datenfehler erzeugen. Erstens: NOT IN (SELECT …) liefert nichts mehr, sobald die Unterabfrage ein einziges NULL enthält – nimm NOT EXISTS. Zweitens: Ein UNIQUE-Index erlaubt in SQLite mehrere NULL-Zeilen, weil zwei Unbekannte nicht als gleich gelten. Wer „höchstens ein Datensatz je Kunde" erzwingen will, braucht zusätzlich NOT NULL.
Datenmodelle, die auch nach Jahren noch richtige Zahlen liefern, entwerfe ich gern mit – melde dich über bymw.de.
Quellen
Du brauchst mehr als ein Snippet?
Ich entwickle Android-Apps in Kotlin und moderne Websites für Selbstständige und kleine Unternehmen — von der ersten Idee bis zum Release.
Projekt anfragen →Verwandte Snippets
Laufende Summe in SQL – SUM() OVER statt Schleife in der Anwendung
Kontostand nach jeder Buchung, kumulierter Umsatz im Jahr, Restbestand nach jeder Entnahme: Alles dieselbe Frage. Mit einer Fensterfunktion beantwortet die Datenbank sie in einer Abfrage – inklusive gleitendem Durchschnitt.
Wie viel mehr als gestern? Differenz zur Vorzeile mit LAG() in SQL
„Wie viele Besucher heute im Vergleich zu gestern?" – der Reflex ist ein Self-Join auf die Vorzeile. Mit der Fensterfunktion LAG() bekommst du den Wert der vorherigen Zeile direkt daneben, ohne die Tabelle ein zweites Mal anzufassen.
Generierte Spalten in SQLite – abgeleitete Werte, die nie veralten
Bruttopreis, Suchspalte in Kleinschreibung, das Jahr aus einem Zeitstempel: Solche Werte doppelt zu pflegen ist eine Einladung an die Inkonsistenz. SQLite kann sie selbst berechnen – und sogar indizieren.