| Firebird Documentation Index → Guida sull'uso di NULL nel linguaggio SQL di Firebird → Frasi condizionali e cicliche |
![]() |
Se l'espressione di test di uno statement IF
risolve a NULL, la clausola
THEN viene saltata e la parte
ELSE (se presente) viene eseguita. In altre parole,
NULL e false di comportano
allo stesso modo in questo contesto, pertanto nelle situazioni in cui si
attende false ma arriva NULL,
non ci sono problemi. Però abbiamo già visto esempi di
NULL arrivati al posto di true
e quindi possono succedere cose strane perchè questo modificherebbe
completamente il funzionamento del
programma!
Gli esempi seguenti mostrano alcuni fra i diabolici (e quindi
perfettamente logici) effetti del NULL negli
statement di IF.
Usando Firebird 2 o successivi, si possono raggirare tutte le
trappole di cui qui stiamo parlando, semplicemente usando l'opratore
[NOT] DISTINCT invece di
«=» e
«<>» !
if (a = b) then Variabile = 'uguali'; else Variabile = 'diversi';
Se a e b sono entrambi
NULL, la Variabile sarà
«diversi» dopo aver eseguito il
codice. La ragione è che l'espressione «a =
b», come abbiamo visto precedentemente, vale
NULL se almeno uno dei termini è
NULL. Con l'espressione di test che vale
NULL, il blocco then non
viene eseguito, ed invece viene eseguito il blocco
else.
if (a <> b) then Variabile = 'diversi'; else Variabile = 'uguali';
In questo caso, Variabile diventerà
«uguali» se a
vale NULL ed invece b no,
oppure viceversa. La spiegazione è analoga a quella dell'esempio
precedente.
Allora come si può mettere in piedi un test di egualglianza che
dia effettivamente il risultato aspettato in tutti
i casi, anche con operandi a NULL? In Firebird 2
abbiamo già visto che si può usare DISTINCT (in
Controllare la
diversità (Firebird 2+)). Nelle precedenti versioni
bisogna scrivere un po' di codice. Questo è mostrato nella sezione Controlli di
eguaglianza, più avanti. Per ora è sufficiente
ricordare che bisogna andare con i piedi di piombo con gli
IF nei condizionali se possono trasformarsi in
NULL.
Altro aspetto da non scordare: un'espressione di controllo a
NULL può comportarsi come un
false in una IF, ma
non vale false. È sempre ed
ancora NULL, ciò significa che la sua negazione è
ancora (oibò!) NULL e non
«true». Come conseguenza, negare
l'espressione di controllo e contemporaneamente scambiare le parti
THEN ed ELSE fra loro può
cambiare il comportamento dello statement IF. Nella
logica binaria, dove esistono solo true e
false, una cosa del genere non potrebbe mai
accadere.
Per mostrare questo fatto, riformuliamo l'ultimo esempio in questo modo:
if (not (a <> b)) then Variabile = 'uguali'; else Variabile = 'diversi';
Nella versione originale, se un operando fosse stato
NULL e l'altro no (quindi intuitivamente
diversi) il risultato sarebbe stato
«uguali». Qui invece è
«diversi». Spiegazione: un operando
è NULL, pertanto «a <>
b» è NULL, pertanto
«not(a <> b)» è ancora
NULL, e quindi viene eseguito
l'ELSE. Ma non c'è modo di gioire del fatto che
questo risultato è corretto mentre il precedente non lo era: in
questa versione riformulata, il risultato è
«diversi» se sono entrambi
NULL, mentre la versione originale almeno
questo lo «imbroccava» giusto.
Naturalmente, finchè nessuno dei due operandi nell'espressione di
test rischia di essere NULL, si possono fare gli
IF come sopra. Inoltre, negando il test e
contemporaneamente scambiando le parti THEN e
ELSE continua a funzionare giusto, per quanto
complessa sia la frase, finchè restano diversi da
NULL gli operandi. È tremendamente perfido quel che
succede quando gli operandi sono quasi sempre
diversi da NULL, cosicchè nella stragran
maggioranza dei casi fila tutto liscio, In queste situazioni, qualche
raro NULL vagante può rimanere nascosto per molto
tempo, rovinando i dati silenziosamente.
Firebird ha introdotto il costrutto CASE nella versione 1.5, con due varianti sintattiche. La prima, detta sintassi semplice;
case <espressione> when <espr1> then <valore1> when <espr2> then <valore2> ... [else <valoredefault>] end
Questa funziona più o meno come il case del Pascal o
lo switch del C:
l'<espressione> viene confrontata con
<espr1>,
<espr2> ecc., finchè non viene trovata
una corrispondenza, nel qual caso viene riportato il valore associato.
Se non c'è corrispondenza e c'è la clausola ELSE,
si riporta il <valoredefault>. Se manca
anche la clausola ELSE, il valore è un bel
NULL.
Importante è sapere che i confronti sono fatti proprio con
l'operatore «=», per cui se
<espressione> è
NULL, ignora tutte le
<esprN>. In questo caso, per avere un
risultato non nullo, bisogna adoperare la clausola
ELSE.
Ad ogni modo, è corretto specificare NULL (o
una qualsiasi espressione valida che possa valere
NULL) per il valore risultante.
La seconda sintassi, o sintassi analitica:
case when <condizione1> then <valore1> when <condizione2> then <valore2> ... [else <valoredefault>] end
Qui, le varie <condizioneN> sono
confronti che possono dare uno dei tre possibili risultati:
true, false, oppure
NULL. Ancora una volta, solo
true va bene, per cui una condizione come «A
= 3» o perfino «A = null» non viene soddisfatta
quando A è NULL. Ricordando che «IS
[NOT] NULL» non riporta mai NULL,
se A è NULL, la condizione «A is null»
riporta true ed il corrispondente
<valoreN> viene riportato. In Firebird
2+ si può usare anche «IS [NOT] DISTINCT
FROM» nelle condizioni, in quanto anche questo
operatore non riporta mai NULL.
Quando si valuta la condizione di una ciclica
WHILE, NULL si comporta come
in una frase IF: se la condizione risolve a
NULL, non si rientra nel ciclo, come se fosse
false. Ancora una volta, vediamo cosa succede con
l'inverso, cioè usando NOT. In una condizione
come
while ( Contatore > 12 ) do
che salterebbe tutto il blocco del ciclo se Contatore è
NULL, negandola con
while ( not Contatore > 12 ) do
farà lo stesso. Può essere che entrambe le situazioni vadano nel verso desiderato, solo che è neccessario essere informati del fatto che questi controlli apparentemente complementari in realtà si comportano allo stesso modo.
Per evitare ogni possibile confusione, bisogna evidenziare che i
cicli di FOR nel PSQL di Firebird hanno una
funzione completamente diversa dai cicli di WHILE,
o dai cicli di for nei
linguaggi di programmazione in generale. Il ciclo di
FOR in Firebird ha la forma:
for<select-statement>into<var-list>do<code-block>
ed esegue il blocco di codice tante volte quante sono le righe
lette attraverso la SELECT. Continua finchè non vengono lette tutte le
righe, a meno che non intervenga una eccezione oppure uno statement tipo
BREAK, LEAVE or
EXIT. Leggere un NULL, o una
riga di campi tutti a NULL,
non ferma il ciclo!
| Firebird Documentation Index → Guida sull'uso di NULL nel linguaggio SQL di Firebird → Frasi condizionali e cicliche |