TATECHATLAS
◎ Deutsch
Daten und Datenbanken

Warum sich NULL in SQL nicht wie null vergleichen lässt

IS NULL, dreiwertige Logik und der Unterschied zwischen COUNT(*) und COUNT(Spalte).

Auf dieser Seite

NULL steht für einen fehlenden oder unbekannten Wert. Ein Vergleich mit NULL über = oder <> liefert unbekannt. WHERE behält nur Zeilen mit einer wahren Bedingung. Fehlende Werte finden Sie mit IS NULL.

Drei Zeilen, zwei bekannte Kontostände

Nehmen Sie die Kontostände 100, 0 und NULL. Es gibt drei Konten, aber nur zwei bekannte Werte. Das dritte Konto könnte einen positiven, negativen oder null betragenden Stand haben. Die Daten geben darüber keine Auskunft. Ein fehlender Wert bedeutet deshalb nicht automatisch, dass kein Geld vorhanden ist.

Die Abfrage mit VALUES benötigt keine vorhandene Tabelle. Sie liefert total_rows = 3, known_balances = 2 und mean_known = 50. Mit einem Ersatzwert von null wird mean_with_zero ungefähr 33,33. Damit ändern Sie den Nenner und die Bedeutung des Durchschnitts, nicht nur seine Darstellung.

WITH balances(balance) AS (
  VALUES (100::numeric), (0::numeric), (NULL::numeric)
)
SELECT COUNT(*) AS total_rows,
       COUNT(balance) AS known_balances,
       AVG(balance) AS mean_known,
       AVG(COALESCE(balance, 0)) AS mean_with_zero
FROM balances;

Fehlende Werte finden

Ein unbekannter Kontostand ist nicht zwangsläufig ein Kontostand von null. In PostgreSQL behandelt IS NOT DISTINCT FROM zwei NULL-Werte beim Vergleich als gleich.

SELECT * FROM accounts WHERE balance IS NULL;
SELECT a IS NOT DISTINCT FROM b;

Gezielt zählen

COUNT(*) zählt Zeilen, COUNT(balance) nur vorhandene Werte. COALESCE(balance, 0) ersetzt fehlende Werte durch null. Verwenden Sie diese Ersetzung nur, wenn sie fachlich sinnvoll ist.

Warum ein Filter Zeilen verschwinden lässt

WHERE balance <> 0 behält hier nur 100. Bei null ist die Bedingung falsch, bei NULL unbekannt. Soll der Bericht sowohl von null verschiedene als auch fehlende Kontostände enthalten, schreiben Sie balance <> 0 OR balance IS NULL.

Eine Negation hilft nicht: NOT(balance = 0) ist bei NULL ebenfalls unbekannt. Legen Sie zuerst fest, wie fehlende Werte behandelt werden sollen. Prüfen Sie bei überraschenden Summen auch die ausgeschlossenen Zeilen; ein syntaktisch gültiger Filter kann fachlich die falsche Auswahl treffen.

Die NULL-Falle bei NOT IN

Sie möchten die Kennung 2 ausschließen, doch die Ausschlussliste enthält zusätzlich NULL. Selbst 1 NOT IN (2, NULL) ergibt unbekannt. SQL kann nicht feststellen, dass 1 von jedem Element verschieden ist; WHERE verwirft die Zeile.

Für eine ausschließende Unterabfrage kann NOT EXISTS mit einer klaren Zuordnungsbedingung geeigneter sein. Es prüft das Fehlen einer passenden Zeile, ohne den NULL-Wert aus einer Ergebnisliste zu übernehmen. Für fehlende Kennungen brauchen Sie trotzdem eine fachliche Regel; ein Syntaxwechsel beantwortet diese Frage nicht.

SELECT 1 NOT IN (2, NULL) AS result;

Fehlende Werte nach einem LEFT JOIN

Ein LEFT JOIN erhält die linken Zeilen und setzt nicht zugeordnete rechte Spalten auf NULL. Eine Bedingung für die rechte Tabelle in WHERE kann diese Zeilen anschließend entfernen. Wenn alle Konten erhalten bleiben und rechts nur aktive Datensätze passen sollen, gehört diese Einschränkung in ON.

Klären Sie vor COALESCE, warum ein Wert fehlt: nicht erhoben, nicht anwendbar oder beim Join nicht gefunden? Dafür können unterschiedliche Kennzeichnungen nötig sein. Zählen Sie zunächst Zeilen, bekannte Werte und Lücken, prüfen Sie die Joins und wählen Sie erst dann einen Ersatzwert für den Bericht.

Was Sie prüfen sollten

  • IS NULL statt = NULL verwenden.
  • Prüfen, ob ein fehlender Wert wirklich null bedeutet.
  • Zeilenanzahl und Anzahl vorhandener Werte vergleichen.

Die Beispiele beziehen sich auf PostgreSQL. NULL-sichere Vergleichsoperatoren unterscheiden sich je nach Datenbanksystem.

Quellen

  1. PostgreSQL: comparison operators ↗
  2. PostgreSQL: conditional expressions ↗
  3. PostgreSQL: aggregate functions ↗
  4. PostgreSQL: SELECT ↗
  5. PostgreSQL: subquery expressions ↗
  6. PostgreSQL: joins and table expressions ↗
Nach oben ↑