Esercitazione sulle sottoquery SQL: tipi, sottoquery correlate ed EXISTS con esempi

  1. Home
  2. Blog
  3. Database e SQL

Database e SQL

Tutorial sulle sottoquery SQL: Tipi, sottoquery correlate ed EXISTS con esempi

Una sottoquery è una query all’interno di un’altra query. Impara ogni tipo e dove si adatta, comprendi correttamente le sottoquery correlate ed evita la trappola NOT IN che silenziosamente non restituisce alcuna riga.

9 min di lettura
Tutorial sulle sottoquery SQL: tipi, sottoquery correlate ed EXISTS con esempi

Punti chiave

  • Una sottoquery è una query annidata all’interno di un’altra e la query esterna ne utilizza il risultato.
  • Le sottoquery possono apparire in WHERE, FROM, SELECT e HAVING.
  • Una sottoquery correlata fa riferimento alla riga esterna, quindi viene valutata logicamente per riga.
  • Preferire da NOT EXISTS a NOT IN quando sono possibili NULL.

Ad alcune domande sono necessari due passaggi per rispondere. “Quali ordini sono sopra la media?” richiede che tu trovi prima la media e poi confronti ogni ordine con essa. Le sottoquery SQL ti consentono di scrivere entrambi i passaggi come un’unica istruzione.

Questo tutorial copre ogni tipo utilizzando due piccole tabelle. Abbiamo eseguito queste query in SQLite 3.50 e i risultati mostrati provengono da tale esecuzione. La sintassi standard delle subquery è ampiamente supportata e segnaliamo i punti in cui i database differiscono.

Cos’è una sottoquery?

Una sottoquery, chiamata anche query interna o annidata, è un’istruzione SELECT inserita all’interno di un’altra istruzione, tra parentesi tonde. Concettualmente, la query interna produce un risultato che viene poi utilizzato dalla query esterna.

SELECT id, total
FROM orders
WHERE total > (SELECT AVG(total) FROM orders);

Le tabelle campione

CREATE TABLE customers (id INT, name TEXT, city TEXT);
CREATE TABLE orders (id INT, customer_id INT, total INT);

INSERT INTO customers VALUES
 (1,'Asha','Pune'), (2,'Ben','Delhi'), (3,'Chitra','Pune'), (4,'Dev','Goa');

INSERT INTO orders VALUES
 (101,1,1200), (102,1,800), (103,2,300), (104,3,2500), (105,3,100);

Tieni presente che Dev non ha ordini. Questo dettaglio è importante per diversi esempi di seguito.

Panoramica dei tipi di sottoquery

Digitare Ritorni Posizione tipica Operatori comuni
Scalare Un valore WHERE, SELECT =, >, <
Multifila Una colonna, molte righe DOVE IN, ANY, ALL
Tabella (derivata) Molte colonne e righe DA Utilizzato come tavolo
Correlato Dipende dalla riga esterna WHERE, SELECT Qualsiasi e EXISTS

Sottoquery scalari

Una sottoquery scalare restituisce esattamente un valore, quindi puoi utilizzarla ovunque abbia senso un singolo valore. Ecco la query di prima, con il suo risultato. La media di tutti gli ordini è 980:

SELECT id, total
FROM orders
WHERE total > (SELECT AVG(total) FROM orders);

-- 101 | 1200
-- 104 | 2500

Non è possibile scrivere WHERE total > AVG(total) perché gli aggregati non sono consentiti direttamente in WHERE. La sottoquery aggira questo problema calcolando il valore separatamente.

Cosa succede se una sottoquery “scalare” restituisce più righe? La maggior parte dei database, inclusi PostgreSQL, MySQL e SQL Server, generano un errore. SQLite no. Nel nostro test ha utilizzato silenziosamente solo la prima riga, che potrebbe nascondere un vero bug, quindi non dipendere da questo comportamento.

DENTRO e NON DENTRO

Quando la query interna restituisce un elenco, utilizzare IN. Questo trova i clienti con almeno un ordine superiore a 1000:

SELECT name FROM customers
WHERE id IN (SELECT customer_id FROM orders WHERE total > 1000);

-- Asha
-- Chitra

NOT IN fa il contrario, trovando clienti che non hanno mai ordinato:

SELECT name FROM customers
WHERE id NOT IN (SELECT customer_id FROM orders);

-- Dev

Qui funziona, ma NOT IN presenta un caso limite pericoloso, che tratteremo di seguito.

Sottoquery in FROM

Una sottoquery in FROM agisce come una tabella temporanea, spesso chiamata tabella derivata. È utile quando è necessario eseguire l’aggregazione due volte, come in questa query per la spesa media per cliente in ciascuna città:

SELECT city, AVG(spent) AS avg_spent
FROM (
  SELECT c.city, SUM(o.total) AS spent
  FROM customers c
  JOIN orders o ON o.customer_id = c.id
  GROUP BY c.id, c.city
) AS per_customer
GROUP BY city;

-- Delhi |  300.0
-- Pune  | 2300.0

La query interna totalizza ciascun cliente e quella esterna calcola la media di tali totali. Assegnare un alias alla tabella derivata è una buona abitudine, poiché alcuni database lo richiedono. Se GROUP BY è nuovo per te, consulta SQL GROUP BY e HAVING.

Sottoquery in SELECT

Una sottoquery scalare può anche aggiungere una colonna calcolata. Questo conta gli ordini per cliente:

SELECT name,
       (SELECT COUNT(*) FROM orders o WHERE o.customer_id = c.id) AS order_count
FROM customers c;

-- Asha   | 2
-- Ben    | 1
-- Chitra | 2
-- Dev    | 0

Dev appare con zero. Un inner join avrebbe eliminato completamente Dev, perché Dev non ha righe corrispondenti. Potresti ottenere lo stesso risultato con LEFT JOIN e GROUP BY, come spiegato in Spiegazione dei join SQL.

Sottoquery correlate

Anche l’esempio precedente è correlato, poiché la query interna fa riferimento a c.id dalla query esterna. Una sottoquery non correlata può essere eseguita da sola. Uno correlato non può, perché dipende da un valore della riga esterna corrente.

Ciò trova ordini superiori alla media dello stesso cliente:

SELECT o.id, o.customer_id, o.total
FROM orders o
WHERE o.total > (
  SELECT AVG(i.total)
  FROM orders i
  WHERE i.customer_id = o.customer_id
);

-- 101 | 1 | 1200
-- 104 | 3 | 2500

Ecco come leggerlo. Per l’ordine 101, la query interna calcola la media degli ordini del cliente 1 (1200 e 800), che restituisce 1000. Poiché 1200 è maggiore, la riga viene mantenuta. Per l’ordine 102 si applica la stessa media, ma 800 non è maggiore, quindi viene eliminato. Il singolo ordine del cliente 2 è uguale alla sua media, quindi viene eliminato anch’esso.

Caratteristica Non correlato Correlato
Utilizza valori di query esterni? No Sì
Può funzionare da solo? Sì No
Esecuzione logica Una volta Una volta per riga esterna
Rischio di prestazione Solitamente basso Può essere alto su tavoli di grandi dimensioni

“Una volta per riga” descrive la logica e gli ottimizzatori di query spesso la riscrivono in qualcosa di molto più efficiente. Tuttavia, vale la pena esaminare una sottoquery correlata lenta. Un indice sulla colonna interna, qui orders.customer_id, può fare una grande differenza. Vedi Spiegazione degli indici SQL.

ESISTE e NON ESISTE

EXISTS pone una domanda sì o no: la query interna restituisce almeno una riga? Può fermarsi alla prima partita e ciò che selezioni all’interno non ha importanza, motivo per cui le persone spesso scrivono SELECT 1.

SELECT c.name FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id);

-- Asha, Ben, Chitra

SELECT c.name FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id);

-- Dev

La trappola NOT IN NULL

Questo è uno dei bug delle subquery più comuni. Abbiamo creato una tabella con un ID reale e un NULL, quindi abbiamo eseguito entrambi i moduli su di essa:

CREATE TABLE r (customer_id INT);
INSERT INTO r VALUES (1), (NULL);

SELECT name FROM customers
WHERE id NOT IN (SELECT customer_id FROM r);
-- (no rows at all)

SELECT c.name FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM r WHERE r.customer_id = c.id);
-- Ben, Chitra, Dev

Perché il primo non restituisce nulla? id NOT IN (1, NULL) significa id <> 1 AND id <> NULL. Qualsiasi confronto con NULL è sconosciuto anziché vero, quindi l’intera condizione non potrà mai essere vera. Non sono presenti messaggi di errore né avvisi, solo un risultato vuoto che sembra plausibile.

Suggerimento: utilizzare NOT EXISTS per anti-join per impostazione predefinita. Se usi NOT IN, aggiungi WHERE customer_id IS NOT NULL all’interno della sottoquery.

Sottoquery vs JOIN vs CTE

Molte query possono essere scritte in più di un modo. In precedenza abbiamo trovato clienti con un ordine di grandi dimensioni utilizzando IN. Ecco lo stesso risultato con un join:

SELECT DISTINCT c.name FROM customers c
JOIN orders o ON o.customer_id = c.id AND o.total > 1000;

-- Asha
-- Chitra

Si noti il DISTINCT. Un’unione può produrre duplicati se un cliente ha diverse corrispondenze, mentre IN e EXISTS no.

Utilizzare Quando
ISCRIVITI Sono necessarie colonne da entrambe le tabelle
DENTRO o ESISTE Devi solo filtrare in base all’esistenza o meno di una corrispondenza
Tabella derivata Devi prima aggregare e rimane breve
CTE La logica prevede diversi passaggi o viene utilizzata più di una volta
Funzione finestra Hai bisogno di una figura per gruppo accanto a ciascuna riga

Le prestazioni variano in base al database e ai dati e gli ottimizzatori moderni spesso producono piani simili. Quindi scegli prima la forma più chiara, poi controlla EXPLAIN se risulta essere lenta.

Errori comuni

  • Utilizzando = con un risultato su più righe. Utilizza IN o assicurati che la query interna restituisca una riga.
  • Utilizzando NOT IN su una colonna nullable. Puoi perdere silenziosamente ogni riga.
  • Utilizzo di alias poco chiari. Nelle query correlate, qualifica ogni colonna, altrimenti potrebbe essere associata alla tabella sbagliata.
  • Nidificazione profonda. Tre livelli sono difficili da leggere, quindi convertili in CTE.
  • Aggiungere ORDER BY all’interno. Di solito non ha alcun effetto sul risultato esterno e alcuni database lo rifiutano.

Considerazioni finali

Le sottoquery rappresentano un modo naturale per esprimere domande in due passaggi. Inizia con le sottoquery scalari e IN, quindi esercitati con quelle correlate ragionando su una singola riga esterna alla volta. E ricorda NOT EXISTS ogni volta che sono possibili NULL. Per esercitarti, scrivi una domanda in tre modi, con una sottoquery, un join e un CTE, e confronta sia i risultati che la leggibilità.

Domande frequenti

Cos’è una sottoquery in SQL?

Una sottoquery è un’istruzione SELECT annidata all’interno di un’altra istruzione SQL, tra parentesi. L’istruzione esterna utilizza il risultato come valore, elenco o tabella.

Cos’è una sottoquery correlata?

È una sottoquery che fa riferimento a una colonna della query esterna. Dipende dalla riga esterna corrente, quindi non può essere eseguito da solo.

Cos’è più veloce, una subquery o un JOIN?

Dipende dal database e dai dati e gli ottimizzatori spesso producono piani simili. Scrivi prima la versione più chiara, quindi confronta i piani di esecuzione se le prestazioni contano.

Qual è la differenza tra IN ed EXISTS?

IN confronta un valore con un elenco di risultati. EXISTS controlla se viene restituita qualche riga. Si comportano diversamente con i NULL quando negati.

Una sottoquery può restituire più colonne?

Sì, se utilizzato in FROM come tabella derivata e alcuni database consentono anche confronti di righe. Una sottoquery scalare deve restituire solo una colonna e una riga.

Continua a imparare

Lavora con Stack Quora

Hai bisogno di aiuto con test, WordPress o SEO?

Aiutiamo i team a fornire software migliore e a far crescere i loro siti. Raccontaci a cosa stai lavorando e ti suggeriremo chiaramente il prossimo passo.

Continua a leggere

Altre guide su test, intelligenza artificiale e sviluppo moderno

Ti è piaciuto questo articolo? Scegli un argomento e passa direttamente ai nostri tutorial più recenti.

Intelligenza artificiale

Spiegazione delle allucinazioni dell'intelligenza artificiale: perché i modelli linguistici inventano le cose e come ridurle

Spiegazione delle allucinazioni dell'intelligenza artificiale: cosa sono, perché i modelli linguistici le producono, tipi comuni, come ridurle con b…

Leggi l'articolo →
DevOps

Spiegazione delle variabili d'ambiente: file .env, segreti e configurazione eseguiti correttamente

Variabili d'ambiente spiegate con esempi Python che abbiamo eseguito: lettura e impostazione, perché ogni valore è una stringa, file .env, ereditari…

Leggi l'articolo →
Sviluppo software

Spiegazione della limitazione della velocità API: algoritmi, intestazioni e gestione degli errori 429

La limitazione della velocità dell'API spiegata con una simulazione che abbiamo eseguito: bucket di token, finestra fissa, finestra scorrevole, risp…

Leggi l'articolo →
Automazione dei test

Derisione in Python: unittest.mock, patch e gli errori che fanno mentire i test

Mocking in Python con unittest.mock, testato: Mock e return_value, side_effect, patch dove viene utilizzato un nome, specifiche e autospec, date moc…

Leggi l'articolo →
Test del software

Spiegazione della copertura del codice: copertura di linea o di filiale e cosa significa realmente il 100%.

Copertura del codice spiegata con una vera esecuzione di cover.py: copertura di linea, ramo e condizione, lettura di un rapporto, perché la copertur…

Leggi l'articolo →
Database e SQL

DELETE vs TRUNCATE vs DROP in SQL: differenze, rollback e quando utilizzarli

DELETE vs TRUNCATE vs DROP spiegato con SQL testato: cosa rimuove, rollback, trigger, reimpostazione dell'identità, velocità e come differiscono Pos…

Leggi l'articolo →
Database e SQL

Spiegazione delle visualizzazioni SQL: crea, utilizza ed evita le insidie, con esempi

Viste SQL spiegate con le query che abbiamo eseguito: creazione di una vista, risultati in tempo reale, nascondere colonne, prestazioni, viste aggio…

Leggi l'articolo →
Sviluppo software

Interfaccia TypeScript e tipo: differenze reali, testate con il compilatore

Interfaccia TypeScript vs tipo, verificata con il vero compilatore: cosa possono fare entrambi, cosa può fare solo uno, fusione delle dichiarazioni,…

Leggi l'articolo →
Sviluppo software

Spiegazione di Python *args e **kwargs: funzioni flessibili con esempi chiari

Python *args e **kwargs spiegati con il codice che abbiamo eseguito: compressione e decompressione, ordine dei parametri, argomenti solo per parole …

Leggi l'articolo →
Sviluppo software

Funzioni Lambda Python: sintassi, usi reali e quando non usarli

Funzioni lambda Python spiegate con il codice che abbiamo eseguito: sintassi, ordinamento con chiave, mappa e filtro, chiusure, trap dell'associazio…

Leggi l'articolo →
Intelligenza artificiale

Cosa sono gli agenti AI? Come funzionano, con un semplice ciclo di agenti che puoi eseguire

Cosa sono gli agenti IA? Una semplice guida al ciclo dell'agente, agli strumenti, alla memoria e ai guardrail, con un piccolo ciclo Python che abbia…

Leggi l'articolo →
DevOps

Monitoraggio vs osservabilità: la vera differenza, con log, metriche e tracce

Spiegazione del monitoraggio e dell'osservabilità: errori noti e sconosciuti, log, metriche e tracce, una procedura dettagliata sugli incidenti di p…

Leggi l'articolo →
DevOps

Volumi Docker e montaggi di associazione: mantenere i dati del contenitore nel modo giusto

Volumi Docker e montaggi di collegamento spiegati: perché i dati del contenitore scompaiono, volumi denominati, montaggi di collegamento, tmpfs, ese…

Leggi l'articolo →
Automazione dei test

TDD vs BDD: differenze chiave, una caratteristica costruita in entrambi i modi e quando utilizzarli ciascuno

TDD vs BDD spiegato con una funzione di sconto costruita in entrambi i modi: refactoring rosso-verde con il codice che abbiamo eseguito, scenari "Da…

Leggi l'articolo →
Test del software

Test di accettazione da parte dell'utente (UAT): processo, lista di controllo ed esempi reali

Spiegazione del test di accettazione dell'utente: chi lo esegue, in cosa differisce dal QA, un processo in sette fasi, un modello di test case UAT, …

Leggi l'articolo →
Database e SQL

Spiegazione dei valori SQL NULL: IS NULL, COALESCE, NULLIF e le trappole da evitare

Valori SQL NULL spiegati con le query che abbiamo eseguito: logica a tre valori, IS NULL, COALESCE, NULLIF, come aggregazioni, join e ordinamento tr…

Leggi l'articolo →
Database e SQL

Istruzione SQL CASE WHEN: sintassi, esempi reali ed errori comuni

SQL CASE WHEN spiegato con le query che abbiamo eseguito: moduli semplici e cercati, ordine delle condizioni, pivot con SUM, trucchi COUNT, ordiname…

Leggi l'articolo →
Sviluppo software

Cos'è il JWT? Token Web JSON spiegati con un esempio funzionante

Cos'è il JWT? Spiegazione dei token Web JSON: le tre parti, un token creato e manomesso nel codice che abbiamo eseguito, JWT e sessioni, archiviazio…

Leggi l'articolo →
Sviluppo software

Spiegazione del loop di eventi JavaScript: stack di chiamate, microtask e timer con esempi

Il ciclo di eventi JavaScript spiegato con il codice che abbiamo eseguito: stack di chiamate, code di attività e microtask, perché setTimeout 0 non …

Leggi l'articolo →
Sviluppo software

Spiegazione dei generatori Python e del rendimento: risparmia memoria con esempi reali

Generatori Python e rendimento spiegati con il codice che abbiamo eseguito: come vengono messi in pausa e ripresi, misurato il risparmio di memoria,…

Leggi l'articolo →
Intelligenza artificiale

Cosa sono gli incorporamenti? I vettori e la somiglianza del coseno spiegati semplicemente

Cosa sono gli incorporamenti? Una guida in linguaggio semplice ai vettori, alla similarità del coseno e alla ricerca semantica, con un piccolo esemp…

Leggi l'articolo →
DevOps

Distribuzione blu-verde rispetto a quella canaria: differenze, compromessi e come scegliere

Spiegazione della distribuzione blu-verde e canary: come funziona ciascuna, costi, rischi e rollback, bucket utente stabile che abbiamo testato, mig…

Leggi l'articolo →
Automazione dei test

Test instabili: quali sono le cause e come risolverli definitivamente

Test instabili spiegati con demo che abbiamo eseguito: le cause principali, come confermarne e diagnosticarne uno, soluzioni adeguate per tempistich…

Leggi l'articolo →
Test del software

Test di carico e test di stress: differenze, metriche ed esempi k6

Spiegazione del test di carico e dello stress test: cosa dimostra ciascuno, test di picco e di assorbimento, le metriche che contano, perché le medi…

Leggi l'articolo →
Test del software

Caso di test e scenario di test: differenze chiave, esempi e quando utilizzarli

Spiegazione del caso di test e dello scenario di test: definizioni chiare, una tabella affiancata, una funzione di reimpostazione della password pre…

Leggi l'articolo →
Database e SQL

Ottimizzazione delle query SQL: leggi i piani di esecuzione e correggi query lente, con tempistiche

Ottimizzazione delle query SQL testata su un milione di righe: leggi i piani di esecuzione, aggiungi gli indici corretti, correggi filtri non aggior…

Leggi l'articolo →
Database e SQL

SQL UNION vs UNION ALL: differenze, esempi e prestazioni testate

SQL UNION vs UNION ALL spiegati con le query che abbiamo eseguito: duplicati, regole seguite da entrambi, un vero bug di dati, prestazioni misurate,…

Leggi l'articolo →
Sviluppo software

Tutorial sull'API di recupero JavaScript: GET, POST, gestione degli errori e timeout

Un tutorial sull'API JavaScript Fetch testato su un server locale: GET e POST, perché 404 non genera eccezioni, un helper riutilizzabile, stringhe d…

Leggi l'articolo →
Sviluppo software

Gestione dei file Python: lettura, scrittura, CSV, JSON e pathlib con esempi

Gestione dei file Python spiegata con il codice che abbiamo eseguito: modalità aperte, con, lettura di file di grandi dimensioni, CSV, JSON, pathlib…

Leggi l'articolo →
Sviluppo software

Classi e oggetti Python: tutorial OOP con esempi reali

Classi e oggetti Python spiegati con il codice che abbiamo eseguito: __init__, metodi, attributi di classe e istanza, ereditarietà, proprietà, metod…

Leggi l'articolo →
Intelligenza artificiale

Che cos'è un modello linguistico di grandi dimensioni? Come funzionano i LLM, spiegato senza esagerazioni

Cos’è un modello linguistico ampio? LLM spiegati in un linguaggio semplice: token, incorporamenti, attenzione, formazione, finestre di temperatura e…

Leggi l'articolo →
DevOps

Comandi Linux per principianti: la guida essenziale del terminale con esempi

Comandi Linux per principianti, raggruppati per attività: navigazione, file, visualizzazione log, grep, pipe, permessi, processi e archivi, con un e…

Leggi l'articolo →
Automazione dei test

Selenio vs drammaturgo: differenze chiave, codice a confronto e quale scegliere

Selenium vs Playwright a confronto: architettura, attesa, supporto per browser e linguaggio, debug, lo stesso test in entrambi gli strumenti, consig…

Leggi l'articolo →
Test del software

Partizionamento di equivalenza: tecnica di progettazione dei test spiegata con esempi pratici

Spiegazione del partizionamento di equivalenza: come trovare partizioni valide e non valide, esempi funzionanti per età, password e coupon, test che…

Leggi l'articolo →
Test del software

Gravità vs priorità nel test del software: differenze, matrice ed esempi reali

Spiegazione della gravità e della priorità nei test del software: definizioni chiare, una matrice a quattro vie con esempi, scale di livello, chi im…

Leggi l'articolo →
Database e SQL

Esercitazione sulle sottoquery SQL: tipi, sottoquery correlate ed EXISTS con esempi

Un tutorial sulle sottoquery SQL con le query che abbiamo eseguito: sottoquery scalari, IN, FROM e SELECT, sottoquery correlate, EXISTS, trap NOT IN…

Leggi l'articolo →
Database e SQL

Spiegazione di transazioni SQL e ACID: COMMIT, ROLLBACK e livelli di isolamento

Transazioni SQL e ACID spiegate con un bonifico bancario eseguito: COMMIT, ROLLBACK, punti di salvataggio, blocchi, livelli di isolamento, letture s…

Leggi l'articolo →
Sviluppo software

Git merge vs rebase: differenze, esempi e quando usare ciascuno

Git merge vs rebase spiegato con output reale del log, una tabella di confronto, la regola d'oro del rebase e un flusso di lavoro sicuro per i team.

Leggi l'articolo →
Sviluppo software

List comprehension in Python: sintassi, esempi e quando evitarla

List comprehension in Python spiegata: sintassi, filtri, if-else, cicli annidati, tempi misurati e i casi in cui un normale ciclo for è meglio.

Leggi l'articolo →
Database e SQL

SQL JOIN spiegati con esempi (Inner, Left, Right, Full)

Capisci i JOIN SQL passo dopo passo con tabelle e query di esempio: INNER, LEFT, RIGHT, FULL, CROSS e SELF, con errori comuni e consigli sulle prest…

Leggi l'articolo →