0Pricing
SQL Interview Prep · Lezione

Riscrivere le sottoquery correlate come join

Trasformare la logica correlata in join o funzioni finestra per migliorare le prestazioni

Riscrivere le sottoquery correlate come join è una lezione SQL Interview Prep gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento SQL Interview Prep, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso SQL Interview Prep include 4 lezioni in totale.

Perché riscrivere una query

Le sottoquery correlate sono leggibili, ma possono essere lente: la query interna potrebbe essere eseguita una volta per ogni riga esterna. Gli intervistatori spesso chiedono di riscriverne una usando un join o una funzione finestra per migliorarne le prestazioni.

L'obiettivo è ottenere lo stesso risultato con un'unica scansione dei dati, invece di ripetere le scansioni interne.

Conoscere due o tre schemi di riscrittura e sapere quando ciascuno preserva la correttezza è una competenza fondamentale di livello intermedio.

Schema 1: da EXISTS a INNER JOIN

Un EXISTS correlato che verifica la presenza di almeno una corrispondenza può spesso diventare un INNER JOIN.

Faccia però attenzione: un join può produrre righe esterne duplicate se corrispondono più righe interne. Aggiunga DISTINCT o un'aggregazione per ripristinare una riga per ogni chiave esterna.

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

-- Join rewrite (DISTINCT avoids dupes from fan-out)
SELECT DISTINCT c.customer_id, c.name
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id;

Il problema del fan-out

L'errore più comune nella riscrittura consiste nel dimenticare il fan-out. EXISTS restituisce ogni cliente una sola volta, indipendentemente dal numero di ordini. Un join ingenuo restituisce una riga per ogni ordine, alterando i conteggi.

Se un passaggio successivo esegue COUNT(*) o SUM(amount) sul risultato del join senza raggruppare con attenzione, i numeri saranno errati.

Si chieda sempre: il join può moltiplicare le righe? In tal caso, utilizzi DISTINCT o una GROUP BY per ricondurre il risultato alla forma corretta.

Schema 2: da NOT EXISTS a LEFT JOIN / IS NULL

La riscrittura di un anti-join è uno schema immancabile nei colloqui. Un NOT EXISTS correlato diventa un LEFT JOIN in cui il lato destro è NULL.

Le righe esterne senza corrispondenza ricevono valori NULL sul lato destro; filtrando quel NULL si conservano esattamente le righe senza corrispondenza.

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

-- LEFT JOIN / IS NULL rewrite
SELECT c.customer_id
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.customer_id
WHERE o.customer_id IS NULL;

Scelga una colonna NOT NULL da verificare

Nella riscrittura LEFT JOIN / IS NULL, verifichi una colonna del lato destro che sia mai NULL in presenza di una corrispondenza reale, idealmente la chiave di join o la chiave primaria.

Se verifica una colonna nullable, non può distinguere una vera mancata corrispondenza (nessuna riga) da una riga corrispondente che contiene semplicemente NULL in quella colonna. Questo errore restituisce righe errate.

Utilizzare la chiave di join (qui o.customer_id) o o.order_id garantisce che NULL significhi "nessuna riga corrispondente".

Schema 3: da aggregazione scalare a JOIN + GROUP BY

Un'aggregazione correlata in SELECT può diventare un join con una sottoquery raggruppata (una tabella derivata).

Calcoli una volta l'aggregazione per ogni gruppo, quindi la ricolleghi alle righe di dettaglio. La query interna viene eseguita una sola volta invece che per ogni riga.

-- Correlated scalar aggregate
SELECT e1.name,
       (SELECT MAX(e2.salary) FROM employees e2
        WHERE e2.dept_id = e1.dept_id) AS dept_max
FROM employees e1;

-- Join + GROUP BY rewrite
SELECT e.name, m.dept_max
FROM employees e
JOIN (SELECT dept_id, MAX(salary) AS dept_max
      FROM employees GROUP BY dept_id) m
  ON m.dept_id = e.dept_id;

Schema 4: la riscrittura con una funzione finestra

Spesso la riscrittura più elegante usa una funzione finestra. MAX(salary) OVER (PARTITION BY dept_id) sostituisce completamente l'aggregazione correlata, senza bisogno di un join.

Calcola il valore del gruppo in un'unica scansione e conserva ogni riga di dettaglio. Questa è in genere la risposta che gli intervistatori desiderano maggiormente vedere per le query analitiche.

SELECT name,
       dept_id,
       salary,
       MAX(salary) OVER (PARTITION BY dept_id) AS dept_max
FROM employees;

Riscrittura del massimo N per gruppo

Una sottoquery correlata che seleziona la riga in cima a ogni gruppo (salary = MAX per dept) si riscrive elegantemente con ROW_NUMBER.

Partizioni per gruppo, ordini in base alla metrica e conservi il rango 1. Utilizzi RANK se desidera includere tutte le righe a pari merito in cima.

SELECT name, dept_id, salary
FROM (
    SELECT name, dept_id, salary,
           ROW_NUMBER() OVER (PARTITION BY dept_id
                              ORDER BY salary DESC) AS rn
    FROM employees
) t
WHERE rn = 1;

Quando non riscrivere

La riscrittura non è sempre vantaggiosa. Mantenga la sottoquery correlata quando:

  • Il set esterno è molto piccolo, quindi il costo per riga è trascurabile.
  • La colonna correlata è ben indicizzata e l'ottimizzatore la trasforma già in un semi-join efficiente.
  • Nel codice sottoposto a manutenzione la leggibilità è più importante della micro-ottimizzazione.

Gli ottimizzatori moderni trasformano spesso EXISTS automaticamente in un semi-join. Dica che misurerebbe con EXPLAIN prima di presumere che una riscrittura sia utile.

Verificare l'equivalenza

Dopo qualsiasi riscrittura, verifichi che restituisca le stesse righe e la stessa cardinalità dell'originale.

  • Controlli che i conteggi delle righe coincidano.
  • Controlli che il fan-out del join non abbia introdotto duplicati.
  • Controlli che i casi limite relativi a NULL e ai gruppi vuoti si comportino ancora correttamente.

Un metodo rapido consiste nell'eseguire entrambe le versioni e applicare EXCEPT in entrambe le direzioni; un risultato vuoto significa che coincidono. Gli intervistatori apprezzano chi verifica invece di dare per scontato.

SELECT customer_id FROM query_a
EXCEPT
SELECT customer_id FROM query_b;
-- and the reverse; both empty => equivalent

Riscrivere IN con un JOIN

Una sottoquery IN non correlata può spesso essere riscritta anch'essa con un join, ma vale lo stesso avvertimento sul fan-out. IN elimina i duplicati nell'appartenenza; un join no.

Se l'elenco interno contiene chiavi duplicate, il join ripete le righe esterne. Utilizzi DISTINCT sul lato interno o sul risultato finale per mantenere la semantica di IN.

-- IN subquery
SELECT c.name FROM customers c
WHERE c.customer_id IN (SELECT o.customer_id FROM orders o);

-- Join rewrite, de-duplicated to match IN
SELECT DISTINCT c.name
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id;

Controllo rapido

Scelga la riscrittura con join corretta per un anti-join correlato con NOT EXISTS.

Riepilogo: riscrivere le sottoquery correlate come join

Punti chiave:

  • EXISTS → INNER JOIN (aggiunga DISTINCT per evitare duplicati dovuti al fan-out).
  • NOT EXISTS → LEFT JOIN ... WHERE key IS NULL (verifichi una colonna non nullable).
  • Aggregazione scalare correlata → esegua un JOIN con una tabella derivata raggruppata oppure, meglio ancora, utilizzi una funzione finestra.
  • Elemento massimo per gruppo → ROW_NUMBER (oppure RANK in caso di pari merito).
  • Verifichi l'equivalenza e controlli con EXPLAIN prima di presumere che una riscrittura sia più veloce.

Conoscere entrambe le forme e il problema del fan-out è esattamente ciò che viene verificato nei colloqui per profili di livello intermedio.

Domande Frequenti

La lezione «Riscrivere le sottoquery correlate come join» è gratuita?

Sì — il testo completo di «Riscrivere le sottoquery correlate come join» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso SQL Interview Prep, passa a CoddyKit PRO. Il corso SQL Interview Prep include 4 lezioni in totale.

Cosa imparerò in «Riscrivere le sottoquery correlate come join»?

Trasformare la logica correlata in join o funzioni finestra per migliorare le prestazioni Eserciti SQL Interview Prep con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare SQL Interview Prep?

Non è richiesta alcuna esperienza precedente. SQL Interview Prep su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.

Quanto tempo richiede la lezione «Riscrivere le sottoquery correlate come join»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione SQL Interview Prep?

Sì. Ogni lezione SQL Interview Prep include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Anatomia di una sottoquery correlata
  2. Aggregati per gruppo senza GROUP BY
  3. EXISTS e NOT EXISTS correlati
  4. Riscrivere le sottoquery correlate come join
← Torna a SQL Interview Prep