NULLs mit Zeichenkette
Im SQL-Standard bedeutet ein Wert von NULL praktisch einen undefinierten Wert. Der Erfinder von AskTom, Tom Kyte, hat NULL immer sehr treffend als die "Abwesenheit eines Wertes" beschrieben. Im Grunde können Variablen oder Spaltenwerte entweder einen Wert haben oder eben NULL sein. Wegen dieser ODER-Verknüpfung bedarf es auch einer gesonderten Handhabung von NULL-Werten. Nehmen wir einmal folgendes einfache Beispiel, eine Prüfung der Eingangsparameter einer PL/SQL-Prozedur:
SQL> create or replace procedure fnc_test(p_wert in number)
2 as
3 begin
4 if p_wert<=0 then
5 raise_application_error(-20000, 'p_wert muss >0 sein!');
6 end if;
7 end;
8* /
Procedure FNC_TEST kompiliert
Die Prozedur prüft also, ob der übergebene Wert größer Null (eine runde 0, nicht NULL 😊) ist, andernfalls wird eine Exception ausgelöst. Natürlich muss dieses erwartete Verhalten verifiziert werden:
SQL> exec fnc_test(p_wert => 1);
PL/SQL-Prozedur erfolgreich abgeschlossen.
SQL> exec fnc_test(p_wert => -1);
Fehler beim Start in Zeile: 1 in Befehl -
BEGIN fnc_test(p_wert => -1); END;
Fehlerbericht -
ORA-20000: p_wert muss >0 sein!
ORA-06512: in "ORG_SUP.FNC_TEST", Zeile 5
ORA-06512: in Zeile 1
Wunderbar, es funktioniert! Doch halt, ein Testfall fehlt noch. Was, wenn als Wert NULL übergeben wird?
SQL> exec fnc_test(p_wert => NULL);
PL/SQL-Prozedur erfolgreich abgeschlossen.
Aha, NULL ist offenbar größer als 0? Eher nicht. Warum wirft die Prozedur also keine Exception? Weil Vergleiche von "echten" Werten mit NULL immer FALSE liefern. Und da "p_wert" ja NULL ist, liefert der Vergleich FALSE als Ergebnis und es wird keine Exception geworfen. Die Prozedur muss daher also NULL separat behandeln, damit der Check funktioniert.
SQL> create or replace procedure fnc_test(p_wert in number)
2 as
3 begin
4 if (p_wert is null) or (p_wert<=0) then
5 raise_application_error(-20000, 'p_wert muss >0 sein!');
6 end if;
7 end;
8* /
Procedure FNC_TEST kompiliert
SQL> exec fnc_test(p_wert => NULL);
Fehler beim Start in Zeile: 1 in Befehl -
BEGIN fnc_test; END;
Fehlerbericht -
ORA-20000: p_wert muss >0 sein!
ORA-06512: in "ORG_SUP.FNC_TEST", Zeile 5
ORA-06512: in Zeile 1
Wenden wir dieses Vorgehen doch mal auf Zeichenketten an. Diesmal soll eine Exception geworfen werden, wenn die Zeichenkette einen Wert hat. Wir prüfen also analog zum Beispiel von oben zuerst, ob der Wert nicht NULL ist und dann, ob er ungleich einer leeren Zeichenkette ist.
SQL> create or replace procedure fnc_test(p_wert in varchar)
2 as
3 begin
4 if (p_wert is not null) and (p_wert <> '') then
5 raise_application_error(-20000, 'p_wert muss leer sein!');
6 end if;
7 end;
8* /
Procedure FNC_TEST kompiliert
SQL> exec fnc_test(p_wert => 'Wert');
PL/SQL-Prozedur erfolgreich abgeschlossen.
Was ist passiert? Warum wird keine Exception geworfen? Der Grund steht in der Dokumentation: "Note: Oracle Database currently treats a character value with a length of zero as null. However, this may not continue to be true in future releases, and Oracle recommends that you do not treat empty strings the same as nulls."
Okay, also ein leerer String wird wie NULL behandelt. Das kann sich aber auch mal ändern. Das bedeutet, für diesen Fall war die Idee schon richtig, eine Fallunterscheidung zu machen, allerdings mit den falschen Annahmen. Denn der zweite Vergleich läuft auf den Vergleich "Wert <> NULL" hinaus, was FALSE ergibt anstatt das erwartete TRUE. Es genügt in diesem Fall, die Länge des Strings zu überprüfen. Ist diese >0, dann wird eine Excpetion geworfen. Ist die Länge gleich 0, dann ist das Ergebnis des Vergleichs FALSE. Das Ergebnis des Vegleichs ist auch dann FALSE, wenn der Wert NULL ist, denn in diesem Fall ist auch das Ergebnis von LENGTH() NULL, wie es in der Dokumentation dazu beschrieben ist. Die Prozedur muss also wie folgt aussehen:
SQL> create or replace procedure fnc_test(p_wert in varchar)
2 as
3 begin
4 if length(p_wert) > 0 then
5 raise_application_error(-20000, 'p_wert muss leer sein!');
6 end if;
7 end;
8 /
Procedure FNC_TEST kompiliert
SQL> exec fnc_test(p_wert => 'Wert');
Fehler beim Start in Zeile: 1 in Befehl -
BEGIN fnc_test; END;
Fehlerbericht -
ORA-20000: p_wert darf nicht leer sein!
ORA-06512: in "ORG_SUP.FNC_TEST", Zeile 5
ORA-06512: in Zeile 1
SQL> exec fnc_test(p_wert => '');
PL/SQL-Prozedur erfolgreich abgeschlossen.
SQL> exec fnc_test(p_wert => NULL);
PL/SQL-Prozedur erfolgreich abgeschlossen.
So wird die Prozedur auch noch funktionieren, sollte das Verhalten von leeren Strings geändert werden.
Eine andere einfache Variante, solche Vergleiche zu testen, ist, sie mit Hilfe der DUAL Tabelle zu überprüfen.
SQL> select 1 from dual where 1=1;
1
____
1
SQL> select 1 from dual where 0=null;
Keine Zeilen ausgewählt
SQL> select 1 from dual where null is null;
1
____
1
SQL> select 1 from dual where '' = '';
Keine Zeilen ausgewählt
SQL> select 1 from dual where '' is null;
1
____
1
Der Teufel steckt wie immer im Detail. Trotz mehr als 2 Jahrzehnten Erfahrungen mit Oracle Datenbanken war mir das Verhalten von leeren Zeichenketten so nicht klar. Man lernt immer wieder dazu. Aber vielleicht habe ich es auch schon wieder vergessen 😊.
Kommentare
Keine Kommentare