WHERE filtra le righe una alla volta
Una SELECT senza WHERE restituisce tutte le righe della tabella. Raramente è quello che vuoi. WHERE ti permette di tenere solo le righe che soddisfano una condizione: SQLite scorre la tabella, valuta la condizione su ogni riga e tiene quelle in cui è vera.
Tornano tre righe: Neuromancer, Hyperion e The Martian. La condizione year > 1980 è stata verificata su ogni riga, e sono sopravvissute solo quelle che la soddisfano.
Il modello mentale: WHERE è un filtro che sta tra FROM e le colonne che selezioni. Tutto ciò che risulta vero passa.
Operatori di confronto
Le basi funzionano come ti aspetti:
= per l'uguaglianza, != o <> per "diverso da", e <, <=, >, >= per l'ordine. I confronti tra stringhe usano gli stessi operatori: author = 'Asimov' corrisponde esattamente, carattere per carattere.
Una cosa da sapere: SQL usa gli apici singoli per i letterali di stringa. Le virgolette doppie servono per gli identificatori (nomi di colonne o tabelle). WHERE author = "Asimov" potrebbe funzionare in SQLite per motivi storici, ma non è portabile e può comportarsi male in silenzio quando la "stringa" coincide per caso con il nome di una colonna. Usa sempre gli apici singoli.
AND, OR e parentesi
Le query reali di solito combinano più condizioni. AND richiede che entrambi i lati siano veri; OR ne richiede almeno uno:
La prima query filtra i libri recenti e brevi. La seconda prende i libri di uno dei due autori.
Quando mescoli AND e OR, la precedenza fa brutti scherzi. AND ha la precedenza su OR, quindi:
si legge come Herbert OR (Gibson AND year > 1980): tutti i libri di Herbert a prescindere dall'anno, più i libri di Gibson dopo il 1980. Probabilmente non è quello che intendevi. Racchiudi la tua intenzione tra parentesi:
Nel dubbio, metti le parentesi. All'ottimizzatore delle query non importa, e chi leggerà la query dopo di te ti ringrazierà.
NULL non si comporta come un valore
Questa è la trappola della clausola WHERE in cui tutti cadono almeno una volta. In SQL NULL significa "sconosciuto", e i valori sconosciuti non si possono confrontare. column = NULL non è falso: è NULL, che WHERE tratta come "salta questa riga".
IS NULL e IS NOT NULL sono gli unici operatori che verificano direttamente NULL. Fissateli bene in testa: ogni altro confronto con NULL restituisce NULL e scarta le righe in silenzio.
La stessa regola vale per la negazione. WHERE author != 'Asimov' non restituisce le righe in cui author IS NULL, perché anche NULL != 'Asimov' è NULL. Se vuoi includere i NULL, chiedili esplicitamente: WHERE author != 'Asimov' OR author IS NULL.
IN e BETWEEN: scorciatoie che userai ogni giorno
IN verifica l'appartenenza a una lista. È un modo più pulito di scrivere una catena di OR:
BETWEEN verifica un intervallo, estremi inclusi:
year BETWEEN 1980 AND 2000 è identico a year >= 1980 AND year <= 2000, solo più breve. Una cosa da ricordare: entrambi gli estremi sono inclusi. Se vuoi estremi esclusi, scrivi i confronti per esteso.
Una nota veloce su IN e NULL: WHERE column NOT IN (1, 2, NULL) non restituirà mai nessuna riga, perché confrontare qualsiasi cosa con NULL dà NULL. Togli i NULL dalla lista, oppure gestiscili a parte con IS NULL.
LIKE per il confronto con pattern
LIKE confronta le stringhe con un pattern usando due caratteri jolly:
%corrisponde a qualsiasi sequenza di caratteri (anche vuota)._corrisponde esattamente a un carattere.
Per impostazione predefinita il LIKE di SQLite non distingue maiuscole e minuscole per le lettere ASCII: 'Dune' LIKE 'dune' è vero. È sorprendente se arrivi da Postgres, dove LIKE distingue maiuscole e minuscole e ILIKE è la versione che non le distingue. (SQLite non ha ILIKE.)
Se ti serve un confronto che distingue maiuscole e minuscole, hai due opzioni. Attivare il pragma globale:
PRAGMA case_sensitive_like = ON;
Oppure usare GLOB, che distingue sempre maiuscole e minuscole e usa caratteri jolly in stile Unix (* per qualsiasi sequenza, ? per un carattere):
Qui GLOB 'd*' non troverebbe niente: maiuscole e minuscole contano.
Filtrare le date
SQLite memorizza le date come testo (di solito YYYY-MM-DD o ISO 8601 completo), il che significa che i confronti tra stringhe funzionano anche come confronti tra date, purché tu resti sul formato ISO:
Siccome '2024-06-01' < '2024-11-08' è vero sia come stringhe sia come date, queste query fanno quello che ti aspetti. Se memorizzi le date in qualsiasi altro formato ('15/01/2024', 'Jan 15 2024'), i confronti daranno in silenzio risultati sbagliati. Usa sempre ISO 8601: il te stesso del futuro ne sarà felice.
Per i calcoli sulle date più complicati (estrarre l'anno, confrontare con "oggi"), SQLite ha le funzioni date(), strftime() e julianday(). Le vediamo nel capitolo su date e orari.
Mettere tutto insieme
Una query che usa diverse di queste cose insieme:
Leggila riga per riga: tieni le righe con un anno noto, nell'intervallo, di uno dei due autori oppure abbastanza lunghe, e che non siano una bozza. È la clausola WHERE che fa ciò che sa fare meglio: combinare condizioni piccole e leggibili in filtri precisi.
Due abitudini da tenere:
- Metti ogni condizione su una riga a sé con il suo rientro. Le clausole
WHERElunghe diventano illeggibili in fretta se scritte su un'unica riga gigante. - Commenta l'intenzione quando la condizione non è ovvia.
-- exclude draftsè un'assicurazione che costa poco.
Prossimo passo: operatori e NULL nel dettaglio
La clausola WHERE è fatta per lo più di operatori applicati alle colonne, e NULL cambia in silenzio il comportamento di ogni operatore. La prossima pagina approfondisce l'insieme di operatori di SQLite (aritmetica, concatenazione di stringhe con ||, la famiglia IS, la logica a tre valori) così le sorprese smettono di essere sorprese.
Domande frequenti
Come funziona la clausola WHERE in SQLite?
WHERE filtra le righe di una query verificando una condizione su ciascuna riga. Le righe in cui la condizione è vera vengono tenute; quelle in cui è falsa o NULL vengono scartate. Va subito dopo FROM: SELECT ... FROM table WHERE condition.
Come combino più condizioni in una clausola WHERE di SQLite?
Usa AND e OR. AND richiede che entrambi i lati siano veri; a OR ne basta uno. AND ha la precedenza su OR, quindi racchiudi tra parentesi le condizioni miste per essere esplicito: WHERE (a OR b) AND c.
Perché WHERE column = NULL non funziona in SQLite?
NULL significa "sconosciuto", quindi qualsiasi confronto con = o != restituisce NULL invece di vero o falso, e le righe vengono tenute solo quando la condizione è vera. Usa invece IS NULL e IS NOT NULL. Sono gli unici operatori che verificano direttamente NULL.
In SQLite LIKE distingue maiuscole e minuscole nella clausola WHERE?
Per impostazione predefinita LIKE non distingue maiuscole e minuscole per i caratteri ASCII: 'Hello' LIKE 'hello' è vero. Per una distinzione completa imposta PRAGMA case_sensitive_like = ON; oppure usa GLOB, che distingue sempre maiuscole e minuscole e usa caratteri jolly in stile Unix (* e ?).