Quando una query deve filtrare dati reali, una sola condizione spesso non basta. Capire gli operatori logici SQL permette di combinare criteri, gestire correttamente i valori mancanti e evitare risultati apparentemente inspiegabili. Qui mostro come usare AND, OR e NOT, come funzionano le parentesi, cosa cambia con NULL e quali errori possono rendere una query poco affidabile.
Le regole essenziali per costruire condizioni SQL corrette
- AND richiede che tutte le condizioni siano vere.
- OR restituisce una riga quando almeno una condizione è vera.
-
NOT nega una condizione, ma va usato con attenzione insieme a
NULL. - Le parentesi rendono esplicito l’ordine di valutazione e prevengono filtri ambigui.
-
NULL non equivale a zero o a una stringa vuota e richiede
IS NULLoppureIS NOT NULL.
Come funzionano AND, OR e NOT nelle query
Gli operatori logici collegano espressioni che producono un risultato logico. In una clausola WHERE, quel risultato decide se una riga viene mantenuta oppure esclusa. Per me, la distinzione più importante da imparare subito è questa: AND restringe il risultato, mentre OR lo amplia.
AND quando devono valere tutte le condizioni
Con AND, una riga passa il filtro solo se ogni condizione è vera. È il caso tipico di una ricerca per intervallo, categoria e stato contemporaneamente.
SELECT nome, città, fatturato
FROM clienti
WHERE regione = 'Lazio'
AND fatturato >= 50000
AND attivo = 1;La query seleziona soltanto i clienti del Lazio con un fatturato almeno pari a 50.000 e con account attivo. Se anche una sola delle tre condizioni non è soddisfatta, la riga non appare. Questo rende AND utile per creare segmenti precisi, ma anche pericoloso quando si aggiungono filtri senza controllare quanto si riduce il numero di risultati.
OR quando basta una condizione
OR è adatto quando vuoi includere più alternative. Per esempio, puoi cercare ordini effettuati tramite il canale online oppure tramite l’applicazione mobile.
SELECT id_ordine, cliente_id, canale
FROM ordini
WHERE canale = 'web'
OR canale = 'app';Una riga viene restituita se almeno una delle due condizioni è vera. Quando le alternative riguardano lo stesso campo, spesso IN è più leggibile e più semplice da estendere.
SELECT id_ordine, cliente_id, canale
FROM ordini
WHERE canale IN ('web', 'app');NOT per escludere un caso
NOT nega il risultato di una condizione. Può essere utile per eliminare i clienti inattivi o gli ordini già annullati.
SELECT *
FROM ordini
WHERE NOT stato = 'annullato';In molti casi preferisco scrivere direttamente il confronto opposto, perché risulta più chiaro durante la manutenzione.
SELECT *
FROM ordini
WHERE stato <> 'annullato';Le due forme non sono sempre equivalenti quando il campo può contenere NULL. È proprio questo il punto in cui la logica SQL si differenzia dalla logica booleana che usiamo nella vita quotidiana.
Perché NULL produce risultati inattesi
In SQL, NULL indica che un valore è mancante, sconosciuto o non applicabile. Non significa automaticamente zero, stringa vuota o falso. Una comparazione come prezzo = NULL non restituisce vero, perché il database non può stabilire che un valore sconosciuto sia uguale a un altro valore sconosciuto.
La logica a tre valori
Le condizioni SQL possono produrre tre risultati: TRUE, FALSE oppure UNKNOWN. Quest’ultimo compare spesso quando una condizione coinvolge NULL.
| Condizione | Risultato | Interpretazione |
|---|---|---|
| TRUE AND TRUE | TRUE | Entrambe le condizioni sono soddisfatte |
| TRUE AND UNKNOWN | UNKNOWN | Una condizione non è determinabile |
| FALSE AND UNKNOWN | FALSE | È già sufficiente una condizione falsa |
| TRUE OR UNKNOWN | TRUE | La prima condizione è già sufficiente |
| FALSE OR UNKNOWN | UNKNOWN | Non c’è ancora una condizione vera |
| NOT UNKNOWN | UNKNOWN | Il valore sconosciuto resta non determinabile |
La clausola WHERE conserva soltanto le righe per cui la condizione è TRUE. Le righe con risultato FALSE o UNKNOWN vengono escluse. Questa regola spiega perché una query può restituire meno record del previsto senza mostrare un errore.
Come verificare correttamente i valori mancanti
Per cercare i valori mancanti devi usare IS NULL o IS NOT NULL.
SELECT id, email
FROM clienti
WHERE email IS NULL;Per escludere i record senza email:
SELECT id, email
FROM clienti
WHERE email IS NOT NULL;Questo dettaglio sembra elementare, ma nella pratica è uno degli errori più frequenti. Quando controllo query scritte da chi sta iniziando, trovo spesso = NULL al posto di IS NULL, con il risultato di ottenere una tabella vuota.
Le parentesi decidono il significato della query
Quando AND e OR compaiono nella stessa condizione, il database valuta normalmente AND prima di OR. Non affidarti però alla memoria o all’intenzione implicita. Le parentesi rendono il significato evidente a chi legge e riducono il rischio di filtrare dati sbagliati.
SELECT *
FROM prodotti
WHERE categoria = 'Laptop'
OR categoria = 'Tablet'
AND prezzo < 1000;Questa condizione viene interpretata come:
categoria = 'Laptop'
OR (categoria = 'Tablet' AND prezzo < 1000)Di conseguenza, tutti i laptop vengono inclusi, anche quelli più costosi, mentre solo i tablet sotto i 1.000 euro passano il filtro. Se l’obiettivo era applicare il limite di prezzo a entrambe le categorie, la forma corretta è:
SELECT *
FROM prodotti
WHERE (categoria = 'Laptop' OR categoria = 'Tablet')
AND prezzo < 1000;La differenza può cambiare radicalmente il risultato. In un report commerciale, un errore simile può gonfiare vendite, margini o numero di clienti. Io uso le parentesi anche quando il motore SQL potrebbe interpretare correttamente la query senza di esse, perché la leggibilità è parte della qualità tecnica.
Una regola pratica per evitare ambiguità
Raggruppa sempre le alternative con OR e applica dopo i vincoli comuni con AND. Questo schema è facile da controllare:
WHERE (condizione_alternativa_1 OR condizione_alternativa_2)
AND condizione_comune;La stessa attenzione serve con NOT. Per esempio, queste due condizioni non hanno lo stesso significato:
WHERE NOT stato = 'chiuso' OR priorità = 'alta';WHERE NOT (stato = 'chiuso' OR priorità = 'alta');Nel primo caso puoi includere una pratica chiusa se ha priorità alta. Nel secondo caso escludi tutte le righe chiuse o ad alta priorità. Una coppia di parentesi può trasformare completamente una regola di business.
Operatori di confronto e condizioni composte
Gli operatori logici raramente lavorano da soli. Di solito collegano confronti come =, <>, >, >=, LIKE, BETWEEN e IN. La combinazione corretta dipende dal tipo di domanda che vuoi fare ai dati.
| Operatore | Uso tipico | Esempio |
|---|---|---|
= |
Uguaglianza esatta | stato = 'attivo' |
<> |
Esclusione di un valore | stato <> 'annullato' |
BETWEEN |
Intervallo inclusivo | prezzo BETWEEN 100 AND 500 |
IN |
Elenco di valori ammessi | regione IN ('Lazio', 'Lombardia') |
LIKE |
Ricerca parziale su testo | email LIKE '%@azienda.it' |
IS NULL |
Ricerca di valori mancanti | telefono IS NULL |
Un esempio realistico può combinare più strumenti:
SELECT id, nome, città
FROM clienti
WHERE (città IN ('Roma', 'Milano', 'Torino'))
AND attivo = 1
AND email IS NOT NULL;La query cerca clienti attivi di tre città che hanno un indirizzo email disponibile. Il vantaggio non è solo sintattico: ogni parte esprime una scelta precisa sul dataset e rende il filtro più facile da verificare.
Il caso particolare di BETWEEN
BETWEEN include entrambi gli estremi dell’intervallo. La condizione prezzo BETWEEN 100 AND 500 comprende quindi anche i valori 100 e 500. Con date e orari bisogna essere più cauti, perché un filtro come BETWEEN '2026-01-01' AND '2026-01-31' può escludere parte dell’ultimo giorno se la colonna contiene anche l’orario.
Per intervalli temporali, spesso è più sicuro usare un limite inferiore incluso e uno superiore escluso:
WHERE data_evento >= '2026-01-01'
AND data_evento < '2026-02-01';Questo approccio evita di dover indovinare l’ultimo istante della giornata e funziona bene anche quando il campo memorizza ore, minuti e secondi.
Errori frequenti e prestazioni delle condizioni
Una query logicamente corretta può comunque essere lenta o difficile da mantenere. Il primo errore che controllo è l’uso eccessivo di OR senza parentesi, seguito da confronti incoerenti tra numeri, date e stringhe.
- Confondere AND con OR può restituire troppe righe oppure nessuna riga.
- Usare = NULL impedisce di trovare i valori mancanti.
- Negare una condizione senza parentesi può estendere l’esclusione oltre il previsto.
-
Applicare funzioni alla colonna, come
YEAR(data_evento), può rendere più difficile usare un indice. -
Usare LIKE con un carattere jolly iniziale, come
LIKE '%roma', può ridurre l’efficacia degli indici.
Non dare per scontato che il database valuti le condizioni esattamente nell’ordine in cui le hai scritte. Il motore può scegliere un piano di esecuzione diverso per migliorare i tempi, quindi non usare l’ordine delle condizioni come protezione contro divisioni per zero o dati non validi.
Per verificare l’effetto reale di un filtro, usa il piano di esecuzione con strumenti come EXPLAIN o l’equivalente del tuo database. Un indice sulla colonna filtrata può aiutare molto, ma non risolve ogni caso. La selettività del filtro, la quantità di dati e la forma della query contano altrettanto.
Leggi anche: Relazione molti a molti - tabella ponte e query SQL
XOR e simboli alternativi
Alcuni sistemi, tra cui MySQL, supportano anche XOR, che è vero quando una sola delle due condizioni è vera. Non è però un operatore da usare alla cieca in query destinate a database diversi, perché il supporto e la sintassi possono cambiare.
SELECT *
FROM utenti
WHERE newsletter = 1 XOR notifiche_sms = 1;In una query portabile preferisco spesso esprimere lo stesso concetto con AND, OR e NOT, anche se la formula è più lunga:
WHERE (newsletter = 1 AND notifiche_sms <> 1)
OR (newsletter <> 1 AND notifiche_sms = 1);Anche simboli come &&, || e ! non sono una buona scelta per il codice SQL condiviso tra più sistemi. Le parole chiave AND, OR e NOT sono più leggibili e riducono i problemi legati al dialetto utilizzato.
La regola che rende affidabili le tue condizioni SQL
Quando costruisco un filtro, parto dalla domanda in linguaggio naturale e la trasformo in blocchi semplici. Prima individuo le alternative, poi raggruppo quelle con OR, infine applico con AND i vincoli che devono valere sempre.
Controllo anche la presenza di NULL e provo la query su casi limite, come date agli estremi, campi vuoti e valori inattesi. Bastano pochi test mirati per scoprire errori che una tabella di risultati apparentemente corretta potrebbe nascondere.
La sintassi conta, ma conta ancora di più il significato del filtro. Parentesi esplicite, gestione corretta di NULL e condizioni brevi rendono le query più leggibili, portabili e affidabili nei report e nei progetti di analisi dati.
