NULL Werte und ihre Tücken

Merke: Am besten keine NULL Werte in kritischen Spalten zulassen, die für Vergleichsoperationen genutzt werden

Wenn NULL Werte mit anderen Zeichenketten verglichen werden, so ist das Ergebnis stets falsch. Dies kann zu hoher Frustration und Verwirrung führen, da es nicht das zu erwartende Verhalten wiederspiegelt.

SELECT CASE WHEN NULL = 'test' THEN 1 ELSE 0 END
-- Ergebnis: 0
SELECT CASE WHEN NULL != 'test' THEN 1 ELSE 0 END
-- Ergebnis: immer noch 0

Ein Anwendungsfall

Zurzeit arbeite ich an der Entwicklung eines opens-source datawarehouse stacks. Speziell den ETL Prozess betrachtet, wollen wir meist nur neue, sich geänderte Daten laden. Um Änderungen zwischen zwei Tabellen oder Selektionen zu erkennen, könnte man jede einzelne Spalte einzeln abgleichen, oder aber alle Spalten zu einer einzigen Zeichenkette konkatinieren und diesen dann abgleichen (sehr vereinfacht).

alter Eintrag
ColAColBConcatinationmd5()
helloworldhello_world99b1ff8f11781541f7f89f9bd41c4a17
bingobongobingo_bongod0946da002ae63f07dc9e1c4489b2c91
neuer Eintrag
ColAColBConcatinationmd5()
helloworldhello_world99b1ff8f11781541f7f89f9bd41c4a17
bingobongibingo_bongia303447dc3952485141a4acb6f096f45
Der hash hat sich geändert, weil sich bongo zu bongi geändert hat

Anmerkung: Die Konkatination erfolgte durch postgres’ || Operator.

SELECT md5(ColA||'_'|| ColB) FROM table

Der hash Wert kann zur Detektion von Änderungen wie folgt genutzt werden:

INSERT INTO table
SELECT * FROM table
WHERE old.hash != new.hash -- wenn diese Aussage wahr ist, also sich der hash unterscheidet, gibt es Änderungen
AND old.pk = new.pk

Das Problem ist jedoch, dass bei solch einer Konkatination der gesamte String ein NULL ausgibt, sobald nur eine Spalte einen NULL Wert beinhaltet. Die Verhashung eines NULL Wertes ergibt wiederum ein NULL Wert und wir treffen auf das anfangs eingeführte Problem. Das bedeutet also:

  • Wenn der alte hash NULL ist und der neue hash ein legitimer String, wird die neue Zeile trotzdem nicht geupdated weil keine Änderung detektiert wurde ( old.hash != new.hash resultiert in false)
  • Eine Konkatination wie oben beschrieben wird immer einen NULL Wert ausgeben, sobald eine der Spalten ein NULL Wert beinhaltet. Angenommen wir haben 10 Spalten mit NULL Werten. Zu einem späterem Zeitpunkt erhalten 8 Spalten einen Wert – das Ergebnis des hashes bleibt jedoch NULL, sodass keine Änderungen durch die Logik detektiert wird, selbst dann nicht, wenn COALESCE() genutzt werden würde.

Daher würde, einmal eingefügt, dieser Eintrag mit einem NULL Wert im hash über unsere Logik keine weiteren Änderungen erfahren.

Lösung

  • COALESCE(): Unter Nutzung der Funktion COALESCE() oder ISNULL() (t-sql) ist es möglich bei einem NULL Wert diesen Wert mit einem Standardwert zu ersetzen und den Vergleich ordnungsgemäß auszuführen, kann jedoch in Fälle trotzdem keine Änderungen detektieren. Beispielsweise dann nicht, wenn beide hashes NULL sind.
  • IS DISTINCT FROM: Dieser Operator ist genau dazu da, um einen NULL Wert ordnungsgemäß von einer Zeichenkette zu unterscheiden und auch ein true auszugeben falls dies der Fall ist. Dieser Operator ist nicht in jedem DBMS verfügbar und stößt auf ähnliche Probleme wie das COALESCE()
  • CONCAT(): Das gesamte Problem wäre nicht entstanden, wenn keine NULL Werte im hash zugelassen werden würde. Dies kann bewerkstelligt werden, indem die Funktion CONCAT() zur Stringkonkatination genutzt wird. NULL Werte werden bei der Konkatination durch eine leere Zeichenkette ersetzt, sodass unser hash in keinem Fall NULL annehmen wird.

Anmerkung: Manchmal ist es denkbar, dass das Ergebnis einer Konkatination absichtlich einen NULL Wert annehmen kann z.B. bei einem zusammengesetzten Schlüssel. Zusätzlich noch ein NOT NULL constraint auf diese Spalte und wir haben eine Art Integritätsprüfung. Wenn eine der Komponenten des Schlüssels leer ist, wird der gesamte zusammengesetzte Schlüssel zu einem NULL Wert, welches dann beim einfügen in die Spalte einen Fehler wirft. Dies wäre ein wünschenswertes Verhalten, da ein zusammengesetzter Scshlüssel mit einer fehlenden Schlüsselkomponente eventuell nicht mehr eindeutig ist und in jedem Fall noch einmal nachgeprüft werden sollte.

Kommentar verfassen

Your email address will not be published.