MWCodebymw.de ↗
SQL

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-Schreibweise

IS 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

#SQL#SQLite#NULL#COALESCE#Aggregation

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 →