Corso
WHERE e HAVING sono due clausole essenziali in SQL. Che tu stia scrivendo query molto avanzate o molto semplici, ti capiterà di dover usare entrambe. Puoi pensare a WHERE e HAVING come a due fratelli: svolgono una funzione simile (il filtraggio) e spesso compaiono insieme. Ma, proprio come i fratelli, hanno caratteristiche distinte e ruoli specifici.
Se stai cercando di fare chiarezza tra WHERE e HAVING, ti suggerisco di iscriverti al nostro skill track SQL Fundamentals come ottimo punto di partenza completo. Ti insegnerà le diverse clausole SQL, l’ordine di esecuzione in SQL, come ottimizzare le query SQL e molte altre nozioni fondamentali.
La risposta breve: WHERE vs. HAVING
La risposta breve, che è anche la fonte di confusione per molte persone, è che WHERE lavora sui dati a livello di riga, mentre HAVING opera sui dati raggruppati. Ecco una linea guida:
-
WHEREfiltra le righe prima che avvenga qualsiasi raggruppamento o aggregazione. Si applica alle singole righe e non può essere usato con funzioni di aggregazione. -
HAVINGfiltra i gruppi dopo che il raggruppamento e l’aggregazione sono stati eseguiti. Si applica ai risultati delle funzioni di aggregazione e viene usato in combinazione conGROUP BY.
Un esempio veloce per mostrare la differenza
Vediamo un esempio rapido. Se vuoi seguire con il tuo workflow, puoi scaricare il dataset sugli affitti di immobili da questo repository GitHub.
Supponiamo ora di dover restituire tutte le proprietà nel nostro dataset che hanno un canone di affitto inferiore a $500. Il livello di dettaglio di ciascuna riga nel dataset (una proprietà per riga) corrisponde al livello di dettaglio della condizione della query. Pertanto, usiamo WHERE nella seguente query:
SELECT *
FROM rentals
WHERE rental_price < 500
Il risultato apparirebbe così. Nota che vengono restituite 118 righe.

Semplice filtraggio di righe con WHERE. Immagine dell’autore.
Supponiamo ora di non voler avere una tabella di proprietà, ma di città che hanno un prezzo medio di affitto inferiore a $2.700. Invece di avere ogni riga corrispondente a una proprietà, raggrupperemo le righe e aggregheremo il canone in un prezzo medio di affitto per ciascuna città. Pertanto, useremo HAVING, come in questa query:
SELECT city, AVG(rental_price) AS average_rent
FROM rentals
GROUP BY city
HAVING AVG(rental_price) < 2700;
E il risultato sarebbe questo. Nota che c’è un solo risultato.

Semplice filtraggio di gruppi con HAVING. Immagine dell’autore.
WHERE, HAVING e ordine di esecuzione in SQL
Vediamo che WHERE lavora con il filtraggio a livello di riga, mentre HAVING lavora con il filtraggio a livello di aggregato. Capire questa differenza spiana anche la strada per imparare l’ordine di esecuzione delle clausole in SQL.
WHERE viene valutata prima di GROUP BY e subito dopo FROM (e JOIN se presente); non può gestire alcun raggruppamento o aggregazione. WHERE entra in gioco prima che venga eseguita qualsiasi aggregazione. Per questo opera solo su dati a livello di riga.
Nel frattempo, HAVING arriva dopo l’esecuzione della clausola GROUP BY. Ciò significa che entra in gioco dopo aver trasformato la tabella e averla raggruppata a un livello diverso dalla granularità della tabella di origine. HAVING opera sulla nuova versione trasformata della tabella.
Come usare la clausola WHERE in SQL
Facciamo un passo indietro e guardiamo ciascuna clausola singolarmente. Possiamo iniziare con la clausola WHERE.
Sintassi e istruzioni di WHERE
WHERE può essere usata in tre diverse istruzioni SQL: SELECT, UPDATE e DELETE.
WHERE nelle istruzioni SELECT
Nelle istruzioni SELECT, che sono quelle usate per recuperare i dati dal database, WHERE svolge il suo ruolo diretto nel filtraggio a livello di riga e ha il suo posto ben noto subito dopo la clausola FROM, come possiamo vedere nella seguente query:
SELECT column1, column2… etc.
FROM table_name
WHERE condition;
L’uso di WHERE con SELECT è il punto in cui la clausola WHERE viene più spesso confusa con HAVING.
WHERE nelle istruzioni UPDATE
Inoltre, WHERE svolge un ruolo importante nelle istruzioni UPDATE per individuare la riga in cui deve avvenire l’aggiornamento dei dati, come possiamo vedere con la seguente sintassi:
UPDATE table_name
SET column_name1 = value1, column_name2 = value2… etc.
WHERE condition;
WHERE nelle istruzioni DELETE
WHERE è anche un’aggiunta utile alle istruzioni DELETE per individuare i record (righe) che devono essere eliminati, come vediamo qui:
DELETE FROM table_name
WHERE condition;
Come scrivere le condizioni WHERE
Le condizioni WHERE sono scritte come semplici espressioni logiche. Sono composte da tre parti: variabile/operando, condizione e valore/risultato. Vediamo le opzioni per le condizioni WHERE, che includono sia operatori di confronto che logici.
| Segno operatore | Descrizione | Tipo di dato dell’operando |
|---|---|---|
| = | Uguale a (alla data) | Numerico, Testo, Data/timestamp, Booleano |
| < | Minore di (prima della data) | Numerico, Data/timestamp |
| > | Maggiore di (dopo la data) | Numerico, Data/timestamp |
| <= | Minore o uguale a (alla o prima della data) | Numerico, Data/timestamp |
| >= | Maggiore o uguale a (alla o dopo la data) | Numerico, Data/timestamp |
| <> (!=) | Diverso da (non alla data) | Numerico, Testo, Data/timestamp, Booleano |
| IN | Uguale a più di un valore (in più date) | Numerico, Testo, Data/timestamp, Booleano (anche se poco sensato!) |
| LIKE | Corrisponde a un pattern di testo (con wildcard) | Testo |
| BETWEEN | Appartiene a un intervallo | Numerico, Data/timestamp |
| AND | Combina più condizioni, tutte devono essere vere | Logico (Booleano) |
| OR | Combina più condizioni, almeno una deve essere vera | Logico (Booleano) |
| NOT | Neghere una condizione | Logico (Booleano) |
| IS NULL | Verifica valori null | Tutti i tipi di dato |
| IS NOT NULL | Verifica valori non null | Tutti i tipi di dato |
Ecco un esempio in cui usiamo NOT insieme all’operatore di confronto IN:
SELECT *
FROM rentals
WHERE city NOT IN ('Cairo', 'Giza');
L’istruzione restituirà tutte le proprietà che non si trovano né nella città del Cairo né in quella di Giza.

Proprietà al di fuori del Cairo e di Giza. Immagine dell’autore.
Casi d’uso di WHERE
Sapendo che WHERE opera con le istruzioni SELECT, UPDATE e DELETE, possiamo prevedere che si presti a tre casi d’uso: filtraggio a livello di riga, recupero dati e manipolazione dei dati.
Filtraggio a livello di riga
Possiamo filtrare le righe della tabella in base a una o più condizioni. La seguente query filtra le righe per includere solo le proprietà di tipo villa.
SELECT *
FROM rentals
WHERE type = ‘villa’;

Selezione delle proprietà villa. Immagine dell’autore.
Recupero dati
WHERE può essere usata per recuperare un punto dati specifico che stiamo cercando. È simile al filtraggio a livello di riga ma più mirata. Supponendo che ci serva conoscere l’ID della proprietà disponibile il primo gennaio 2022 al Cairo, possiamo usare la seguente query:
SELECT property_id
FROM rentals
WHERE available_date = '2022-01-01'
AND city = 'Cairo';

Recupero di un punto dati. Immagine dell’autore.
Manipolazione dei dati
Infine, WHERE è un grande aiuto per modificare valori ed eliminare record specifici nel tuo database. Per esempio, supponendo di aver scoperto che la proprietà 171 in realtà non ammette animali, possiamo modificare la colonna pet_friendly usando WHERE in un’istruzione UPDATE come segue:
UPDATE rentals
SET pet_friendly = false
WHERE property_id = 171;
Come usare la clausola HAVING in SQL
Ora è il momento di passare alla clausola HAVING con lo stesso livello di dettaglio.
Sintassi e istruzioni di HAVING
Per prima cosa, dovremmo sapere che la clausola HAVING può essere usata solo nelle istruzioni SELECT. Pertanto, la sua unica sintassi è la seguente:
SELECT grouped_column, aggregate_function(aggregated_column)… etc.
FROM table_name
GROUP BY grouped_column
HAVING condition
Nota che puoi aggiungere più di una colonna di raggruppamento e più di una colonna aggregata. Vedremo esempi più avanti.
Come scrivere le condizioni HAVING
Come WHERE, le condizioni HAVING sono scritte come espressioni logiche, ma con un componente aggiuntivo: la funzione di aggregazione. Quindi, una condizione HAVING è composta da 1) Funzione di aggregazione, 2) variabile/operando, 3) operatore di confronto e 4) valore/risultato.
Funzioni di aggregazione con HAVING
SQL ha principalmente cinque funzioni di aggregazione, più una sesta che è un caso speciale. Queste funzioni sono:
| Funzione di aggregazione | Tipo di dato adatto |
|---|---|
| SUM() | Numerico |
| AVG() | Numerico |
| MIN() | Numerico, Testo, Data/timestamp |
| MAX() | Numerico, Testo, Data/timestamp |
| COUNT() | Numerico, Testo, Data/timestamp, Booleano |
Proviamo un esempio. Qui usiamo la funzione COUNT() con GROUP BY per creare una tabella di frequenza e usiamo la condizione HAVING come filtro. In particolare, ci servono le città citate 150 volte o meno, che in questo contesto hanno a che fare con il numero di registrazioni. Possiamo usare questa query:
SELECT city, COUNT(*) AS properties_count
FROM rentals
GROUP BY city
HAVING COUNT(*) <= 150;

Uso di COUNT() con HAVING. Immagine dell’autore.
Operatori di confronto e logici
HAVING accetta tutti gli operatori di confronto e logici che accetta WHERE. L’unica differenza è che il tipo di dato adatto con HAVING dipende innanzitutto dalla funzione di aggregazione, come vediamo nella tabella sopra.
Casi d’uso di HAVING
A differenza di WHERE, HAVING non può essere usata nelle istruzioni UPDATE e DELETE; HAVING è usata solo per il recupero dei dati. In linea di massima, questo si traduce in due scenari:
Filtraggio a livello di gruppo
Questo è l’uso comune e normale di HAVING. Un esempio è restituire solo le città che hanno prezzi medi di affitto inferiori a $2.700.
SELECT city, AVG(rental_price) AS avg_price
FROM rentals
GROUP BY city
HAVING AVG(rental_price) < 2700;
Filtraggio a riga singola
Questo è un caso meno comune, ma HAVING può essere usata per restituire un’aggregazione a riga singola, come una metrica o un KPI, solo se soddisfa una certa condizione. Questo si può ottenere usando HAVING senza GROUP BY. Nel seguente esempio, restituiamo il prezzo medio di affitto solo se è inferiore a $2.800. Se la misura soddisfa la condizione, avremo un risultato a riga singola. In caso contrario, avremo una tabella vuota.
SELECT AVG(rental_price) as avg_price
FROM rentals
HAVING AVG(rental_price) > 2800;
Combinare WHERE e HAVING
WHERE e HAVING possono essere combinati per filtrare le tabelle sia prima che dopo l’aggregazione. Nell’esempio seguente, restituiamo il conteggio delle proprietà in ciascuna città, contando solo le proprietà pet friendly e filtrando solo le città che hanno più di 80 proprietà.
SELECT city, COUNT(*) AS number_properties
FROM rentals
WHERE pet_friendly = true
GROUP BY city
HAVING COUNT(*) > 80;

Combinazione di base di WHERE e HAVING. Immagine dell’autore.
La differenza tra WHERE e HAVING in termini di performance
Sappiamo che la clausola WHERE viene applicata prima del raggruppamento o dell’aggregazione. Ma dovremmo anche sapere che, per questo motivo, poiché riduce presto nel corso della query il numero di righe elaborate, WHERE è più efficiente per filtrare le singole righe.
Sappiamo invece che la clausola HAVING viene applicata dopo l’aggregazione e filtra il result set in base ai dati raggruppati. Per questo motivo, poiché elabora i dati dopo che tutte le righe sono state raggruppate, in genere è meno efficiente di WHERE, pur restando necessaria per condizioni che coinvolgono funzioni di aggregazione.
Guardiamo questa query:
SELECT city, COUNT(*)
FROM rentals
GROUP BY city
HAVING rental_price < 2700;
Questa query è inefficiente perché la condizione su rental_price non dipende da alcuna aggregazione — filtra le singole righe. La query prima raggrupperà tutti gli affitti per città, li conterà e poi filtrerà il risultato, il che è inefficiente perché elabora righe non necessarie. Per questo motivo, questa sarebbe stata una query più veloce:
SELECT city, COUNT(*)
FROM rentals
WHERE rental_price < 2700
GROUP BY city;
Consigli di ottimizzazione per WHERE e HAVING
Sulla base di quanto visto, possiamo trarre alcune buone pratiche per ottimizzare l’uso delle clausole WHERE e HAVING. Questo è importante se hai dataset di grandi dimensioni.
-
Sii molto selettivo: filtra in base ai valori di cui hai davvero bisogno, e non oltre. Questo aiuterà a ridurre il numero di righe nella vista e quindi a migliorare l’efficienza della query.
-
Sii semplice: elimina condizioni, aggregazioni e conversioni di tipo non necessarie. Tutti questi extra hanno sicuramente un costo computazionale.
-
Filtra presto: se stai effettuando un’aggregazione a livello di gruppo, pre-filtra le righe con
WHEREper rendere più veloce il processo di raggruppamento. Così facendo, ridurrai il numero di righe da elaborare nel raggruppamento.
Tabella di confronto
Ricapitoliamo i concetti in una tabella pratica:
| Confronto | WHERE | HAVING |
|---|---|---|
| Scopo principale | Filtraggio a livello di riga | Filtraggio a livello di gruppo |
| Sintassi di base | SELECT column1, column2... FROM table_name WHERE condition; | SELECT grouped_column, aggregate_function(aggregated_column)... FROM table_name GROUP BY grouped_column HAVING condition; |
| Ordine di valutazione | Prima di GROUP BY | Dopo GROUP BY |
| Istruzioni compatibili | SELECT, UPDATE, DELETE | SELECT |
| Condizioni | Non possono includere funzioni di aggregazione | Devono includere funzioni di aggregazione |
| Casi d’uso | Filtraggio a livello di riga Recupero dati Manipolazione dei dati | Filtraggio a livello di gruppo Filtraggio a riga singola |
| Sottoquery | Può funzionare con sottoquery | Devono essere scritte come CTE |
Conclusione
In tutto l’articolo abbiamo esplorato la differenza principale tra WHERE e HAVING in SQL, ossia che la clausola WHERE filtra le righe prima dell’aggregazione, mentre la clausola HAVING filtra i dati raggruppati dopo l’aggregazione. Abbiamo anche visto alcune differenze meno note, come il fatto che WHERE può lavorare con le istruzioni SELECT, UPDATE e DELETE, mentre HAVING funziona solo con SELECT. Abbiamo parlato anche un po’ di performance.
La SQL Basics Cheat Sheet di DataCamp offre un’utile sintesi delle clausole WHERE e HAVING e alcune indicazioni su come effettuare il filtraggio in SQL. Inoltre, se stai iniziando con SQL, dai un’occhiata allo skill track SQL Fundamentals e al career track Associate Data Analyst in SQL.

Islam è un consulente dati presso The KPI Institute. Con un background nel giornalismo, Islam ha interessi diversi, tra cui scrittura, filosofia, media, tecnologia e cultura.
Domande frequenti
Qual è la differenza tra WHERE e HAVING?
WHERE esegue il filtraggio a livello di riga mentre HAVING esegue il filtraggio a livello di gruppo.
Posso combinare WHERE e HAVING in un’unica query?
Sì, ed è altamente consigliato usare WHERE se hai intenzione di usare HAVING.
WHERE può funzionare con funzioni di aggregazione come HAVING?
No, solo HAVING può lavorare con funzioni di aggregazione.
WHERE e HAVING funzionano con gli stessi operatori di confronto e logici?
Sì, entrambe funzionano con gli stessi operatori, poiché le condizioni WHERE e HAVING sono scritte come espressioni logiche.
Posso usare HAVING senza GROUP BY?
HAVING è generalmente usata dopo il raggruppamento con GROUP BY. L’unica eccezione è il filtraggio di viste a valore singolo (come il calcolo di una metrica).

