Logica condizionale nelle query

Nelle lezioni precedenti abbiamo visto come utilizzare WHERE per filtrare i dati e diverse funzioni per elaborare numeri, date e stringhe. 

SQL permette di andare oltre le semplici condizioni e di produrre risultati diversi in base ai dati. Possiamo, ad esempio, assegnare una categoria a un prodotto in base al prezzo, mostrare un testo diverso a seconda dello stato di un utente oppure sostituire un valore NULL con un altro valore.

Per farlo possiamo utilizzare principalmente CASE, COALESCE(), IFNULL() e IF().

Il costrutto CASE

CASE permette di valutare una o più condizioni e di restituire un risultato diverso in base alla condizione verificata.

Esistono due forme principali di CASE: la sintassi Simple, che confronta una colonna con diversi valori, e la sintassi Searched, che permette di utilizzare condizioni più generiche.

Sintassi Simple di CASE

La forma Simple di CASE confronta un'espressione con diversi valori.

La sintassi è:

CASE espressione
    WHEN valore1 THEN risultato1
    WHEN valore2 THEN risultato2
    ELSE risultato_default
END

Per esempio, supponiamo di avere una colonna stato che può contenere i valori 1, 2 o 3 e identifica lo stato di un utente:

SELECT nome,
       CASE stato
           WHEN 1 THEN 'Attivo'
           WHEN 2 THEN 'Sospeso'
           WHEN 3 THEN 'Eliminato'
           ELSE 'Sconosciuto'
       END AS descrizione_stato
FROM utenti;

SQL confronta il valore di stato con ciascun valore indicato dopo WHEN. Se stato vale 1, restituisce Attivo; se vale 2, restituisce Sospeso; se vale 3, restituisce Eliminato.

ELSE è facoltativo in CASE e permette di indicare il risultato da restituire quando nessuna delle condizioni precedenti viene soddisfatta. Sapendo che stato può arrivare fino a 3, la query può essere riassunta così: 

SELECT nome,
       CASE stato
           WHEN 1 THEN 'Attivo'
           WHEN 2 THEN 'Sospeso'
           ELSE 'Eliminato'
       END AS descrizione_stato
FROM utenti;

Se omettiamo ELSE e nessuna condizione viene soddisfatta, CASE restituisce NULL.

Sintassi Searched

La forma Searched di CASE permette invece di utilizzare vere e proprie condizioni dopo WHEN.

La sintassi è:

CASE
    WHEN condizione1 THEN risultato1
    WHEN condizione2 THEN risultato2
    ELSE risultato_default
END

Per esempio, possiamo classificare i prodotti in base al loro prezzo:

SELECT nome, prezzo,
       CASE
           WHEN prezzo < 20 THEN 'Economico'
           WHEN prezzo < 100 THEN 'Medio'
           ELSE 'Costoso'
       END AS categoria_prezzo
FROM prodotti;

Nell'esempio:

  • Se il prezzo è inferiore a 20, viene restituito Economico;
  • Se il prezzo è almeno 20 ma inferiore a 100, viene restituito Medio;
  • In tutti gli altri casi viene restituito Costoso.

La forma Searched di CASE è particolarmente utile perché possiamo utilizzare operatori di confronto e condizioni (come quelle in JavaScript) anche complesse.

L'ordine delle condizioni

Le condizioni di un CASE vengono valutate dall'alto verso il basso. Quando viene trovata la prima condizione vera, SQL restituisce il relativo risultato e non valuta le condizioni successive.

Per questo motivo, è importante ordinarle correttamente. Ad esempio, questa query SQL restituirà sempre e solo Medio o Economico, mai Costoso:

SELECT prezzo,
       CASE
           WHEN prezzo >= 50 THEN 'Medio'
           WHEN prezzo >= 100 THEN 'Costoso'
           ELSE 'Economico'
       END AS categoria
FROM prodotti;

Ad esempio, un prodotto con un prezzo di 120 soddisfa già la prima condizione, quindi viene classificato come Medio e non come Costoso.

Utilizzare CASE nelle query

CASE non è una clausola indipendente come SELECT o WHERE: è un'espressione che possiamo inserire in diverse parti di una query.

CASE in SELECT

L'utilizzo più comune di CASE è all'interno di SELECT, per creare un valore calcolato in base ai dati.

Per esempio:

SELECT nome, prezzo,
       CASE
           WHEN prezzo < 50 THEN 'Basso'
           WHEN prezzo < 100 THEN 'Medio'
           ELSE 'Alto'
       END AS fascia_prezzo
FROM prodotti;

In questo caso fascia_prezzo non è una colonna presente nella tabella: viene calcolata dalla query.

Possiamo utilizzare CASE anche insieme alle funzioni viste nelle lezioni precedenti:

SELECT nome,
       CASE
           WHEN CHAR_LENGTH(nome) > 10 THEN 'Nome lungo'
           ELSE 'Nome breve'
       END AS tipo_nome
FROM utenti;

CASE in WHERE

Possiamo utilizzare un'espressione CASE anche all'interno di WHERE, anche se spesso una normale combinazione di condizioni con AND e OR è più semplice e leggibile.

In questo caso la soglia del prezzo cambia in base alla categoria del prodotto:

SELECT * FROM prodotti
WHERE CASE
          WHEN categoria = 'PC' THEN prezzo > 500
          WHEN categoria = 'Tablet' THEN prezzo > 200
          ELSE FALSE
      END;

CASE in ORDER BY

Un altro utilizzo molto interessante è all'interno di ORDER BY. Possiamo utilizzare CASE per creare un ordinamento personalizzato.

Supponiamo, ad esempio, di voler mostrare prima gli utenti attivi, poi quelli sospesi e infine gli altri:

SELECT nome, stato
FROM utenti
ORDER BY CASE
             WHEN stato = 'attivo' THEN 1
             WHEN stato = 'sospeso' THEN 2
             ELSE 3
         END;

SQL assegna temporaneamente un numero a ogni stato e utilizza questi numeri per stabilire l'ordine.

Possiamo anche combinare CASE con un secondo criterio. In questo esempio gli utenti vengono prima ordinati in base allo stato e, all'interno dello stesso stato, alfabeticamente per nome:

SELECT nome, stato FROM utenti
ORDER BY CASE
             WHEN stato = 'attivo' THEN 1
             WHEN stato = 'sospeso' THEN 2
             ELSE 3
         END,
         nome ASC;

Gestire valori alternativi

Nella lezione sulle condizioni avanzate abbiamo visto che NULL rappresenta un valore assente o sconosciuto e che deve essere gestito con IS NULL e IS NOT NULL, anziché con = NULL.

Quando invece vogliamo sostituire un valore NULL con un valore alternativo, possiamo utilizzare COALESCE().

La sintassi è: COALESCE(valore1, valore2, valore3, ...) e la funzione restituisce il primo valore che non è NULL.

Ad esempio, se il numero di telefono di un utente non è disponibile:

SELECT nome, COALESCE(telefono, 'Non disponibile') AS telefono
FROM utenti;

Nell'esempio, se telefono contiene un numero, viene mostrato quel numero. Se invece contiene NULL, viene mostrato Non disponibile.

Puoi anche usare diverse alternative in COALESCE():

SELECT nome, COALESCE(telefono, cellulare, 'Non disponibile') AS contatto
FROM utenti;

SQL controllerà i valori nell'ordine: telefono, cellulare, e poi assegna Non disponibile.

Funzione IFNULL()

MySQL mette a disposizione anche IFNULL(), una funzione specifica per gestire un singolo valore NULL. È una versione semplificata di COALESCE().

La sintassi è: IFNULL(valore, alternativa). Se valore non è NULL, viene restituito il valore originale. Se invece è NULL, viene restituita l'alternativa.

Vediamo un esempio:

SELECT nome, IFNULL(telefono, 'Non disponibile') AS telefono
FROM utenti;

La differenza principale da COALESCE() è che IFNULL() accetta due argomenti, mentre COALESCE() può gestirne diversi.

Funzione IF()

MySQL mette a disposizione anche la funzione IF(), che permette di scegliere tra due risultati in base a una condizione.

La sintassi è: IF(condizione, valore_se_vero, valore_se_falso). Se condizione è TRUE, allora viene assegnato valore_se_vero, se è FALSE viene assegnato valore_se_falso.

Per esempio, possiamo usare IF() per restituire Maggiorenne o Minorenne in base a eta dell'utente:

SELECT nome, IF(eta >= 18, 'Maggiorenne', 'Minorenne') AS categoria
FROM utenti;

Prova!Completa gli spazi vuoti con il testo appropriato.
-- Se stato è 1, allora restituisci 'true'
SELECT stato
       1 THEN 'true'
       ELSE 'false'
   END AS utente_verificato
FROM utenti;

Prova! Scegli l'opzione corretta tra quelle elencate.
-- Se è NULL, imposta un valore alternativo
SELECT nome, ________(soprannome, nome, 'Non disponibile') AS nome
FROM utenti;