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.
- 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,SELECTeHAVING. - Una sottoquery correlata fa riferimento alla riga esterna, quindi viene valutata logicamente per riga.
- Preferire da
NOT EXISTSaNOT INquando 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. UtilizzaINo assicurati che la query interna restituisca una riga. - Utilizzando
NOT INsu 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 BYall’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
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.
Altre guide su test, intelligenza artificiale e sviluppo moderno
Ti è piaciuto questo articolo? Scegli un argomento e passa direttamente ai nostri tutorial più recenti.
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 →DevOpsSpiegazione 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 softwareSpiegazione 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 testDerisione 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 softwareSpiegazione 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 SQLDELETE 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 SQLSpiegazione 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 softwareInterfaccia 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 softwareSpiegazione 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 softwareFunzioni 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 artificialeCosa 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 →DevOpsMonitoraggio 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 →DevOpsVolumi 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 testTDD 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 softwareTest 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 SQLSpiegazione 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 SQLIstruzione 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 softwareCos'è 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 softwareSpiegazione 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 softwareSpiegazione 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 artificialeCosa 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 →DevOpsDistribuzione 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 testTest 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 softwareTest 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 softwareCaso 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 SQLOttimizzazione 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 SQLSQL 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 softwareTutorial 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 softwareGestione 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 softwareClassi 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 artificialeChe 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 →DevOpsComandi 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 testSelenio 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 softwarePartizionamento 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 softwareGravità 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 SQLEsercitazione 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 SQLSpiegazione 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 softwareGit 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 softwareList 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 SQLSQL 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 →