Firebird Documentation IndexGuida sull'uso di NULL nel linguaggio SQL di Firebird → Frasi condizionali e cicliche
Firebird Home Firebird Home Indietro: Le funzioni di aggregazioneFirebird Documentation IndexRisali: Guida sull'uso di NULL nel linguaggio SQL di FirebirdAvanti: Chiavi ed indici univoci

Frasi condizionali e cicliche

NULL negli statement di IF
Le frasi con CASE
Le cicliche WHILE
Le cicliche FOR

NULL negli statement di IF

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.

Suggerimento

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.

Le frasi con CASE

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.

Le cicliche WHILE

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.

Le cicliche FOR

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!

Indietro: Le funzioni di aggregazioneFirebird Documentation IndexRisali: Guida sull'uso di NULL nel linguaggio SQL di FirebirdAvanti: Chiavi ed indici univoci
Firebird Documentation IndexGuida sull'uso di NULL nel linguaggio SQL di Firebird → Frasi condizionali e cicliche