SQL per Colloqui Tecnici: 20 Query Esistenziali con Dataset, Esercizi e Soluzioni per Developer e Data Analyst

Cerca:

Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors
come superare test SQL colloquio di lavoro

SQL per i colloqui tecnici

Durante un colloquio tecnico per un ruolo data-oriented, il momento dello schermo condiviso e del live coding in SQL è quasi sempre il vero punto di svolta. Non perché il recruiter si aspetti di vederti scrivere una query d’insieme di cinquanta righe a mente, ma perché in meno di cinque minuti valuterà la tua capacità di tradurre un requisito aziendale ambiguo in rigorosa logica relazionale.

Quando ti chiedono di calcolare una metrica su una tabella, non stanno valutando solo se ricordi la sintassi di un comando: vogliono vedere se il tuo primo istinto è chiederti come si comporteranno le chiavi esterne, se hai considerato l’impatto dei valori nulli e se sai esattamente quale differenza passa tra un filtro applicato sul record grezzo o sul dato aggregato.

Molti candidati inciampano sui fondamentali proprio sotto stress.

Questa guida nasce per evitare quell’effetto schermo bianco: abbiamo selezionato  le 20 query che tornano più spesso nei colloqui per Developer, Data Analyst e Data Scientist, ognuna spiegata, scritta e verificata su un dataset di esempio.

  • 20 query di riferimento
  • 1 dataset condiviso
  • 5 aree tematiche

Perché SQL conta

  • Gestisce e interroga i dati nei database relazionali

  • È la competenza tecnica più testata nei colloqui

  • Richiesta per ruoli Developer, Analyst, Data Scientist

Come usare questa guida

  • Ogni query usa lo stesso identico dataset
  • Prova a prevedere l’output prima di leggerlo
  • In fondo trovi 3 esercizi di verifica con soluzione
Pubblicità

Il dataset di esempio

Due tabelle, usate in ogni esempio

Per rendere concreto ogni output, tutte le query di questa guida lavorano sulle stesse due tabelle: employees e departments.

employees

Colonna Tipo Descrizione
id PK, int Chiave primaria
name text Nome del dipendente
salary int Stipendio annuo
department text Nome del reparto (testo)
dept_id FK → id Chiave esterna verso departments
hire_date date Data di assunzione

10 righe4 reparti

departments

Colonna Tipo Descrizione
id PK, int Chiave primaria
department_name text Nome esteso del reparto

La colonna department (testo) resta separata da dept_id (chiave esterna) solo per didattica: le query 1–12 filtrano direttamente sul testo, le query 13–15 mostrano invece il JOIN “corretto” tramite dept_id.

Righe di esempio: Anna Rossi (HR, 45000), Marco Bianchi (IT, 62000), Luca Neri (IT, 71000), Matteo Greco (Sales, 75000), tra le altre.


01 — Selezionare e filtrare

01 — Select All Data

Restituisce tutte le colonne e tutte le righe.

È il punto di partenza per esplorare una tabella.

SELECT * FROM employees;

Output

Tutte e 10 le righe, con tutte le colonne (id, name, salary, department, dept_id, hire_date).

02 — Select Specific Columns

Selezionare solo le colonne necessarie rende la query più leggera e l’output più leggibile.

SELECT name, salary FROM employees;

Output

name salary
Anna Rossi 45000
Marco Bianchi 62000

03 — WHERE — filtrare le righe

WHERE filtra le righe in base a una condizione, applicata prima di qualsiasi raggruppamento.

SELECT * FROM employees
WHERE salary > 50000;

Output

6 righe: Marco Bianchi (62000), Giulia Verdi (58000), Luca Neri (71000), Paolo Gallo (55000), Davide Romano (67000), Matteo Greco (75000).

04 — AND / OR — condizioni multiple

AND richiede che tutte le condizioni siano vere; OR che almeno una lo sia.

SELECT * FROM employees
WHERE department = 'IT' AND salary > 60000;

Output

Marco Bianchi (62000), Luca Neri (71000), Davide Romano (67000) — Giulia Verdi (58000) è esclusa perché sotto soglia.

05 — LIKE — pattern matching

Cerca un pattern testuale: % sostituisce zero o più caratteri, _ un singolo carattere.

SELECT * FROM employees
WHERE name LIKE 'A%';

Output

Anna Rossi — l’unico nome che inizia per “A”. Altri pattern utili: LIKE ‘%i’ (finisce per “i”), LIKE ‘%ar%’ (contiene “ar”).

06 — BETWEEN — valori in un intervallo

Seleziona valori compresi tra due estremi, inclusi.

SELECT * FROM employees
WHERE salary BETWEEN 30000 AND 70000;

Output

Tutti tranne Matteo Greco (75000), che supera il limite superiore.

Forse potrebbe interessarti anche:  Excel e Python: Perché l'integrazione (e non la scelta) è il futuro della Data Analysis

07 — IN — corrispondenza in una lista

Alternativa più leggibile a una catena di OR ripetuti sulla stessa colonna.

SELECT * FROM employees
WHERE department IN ('HR', 'IT');

Output

6 righe (Anna Rossi, Marco Bianchi, Giulia Verdi, Luca Neri, Sara Conti, Davide Romano) — equivalente a department = ‘HR’ OR department = ‘IT’.


02 — Ordinare e raggruppare

08 — ORDER BY — ordinare i risultati

Ordina l’output finale. DESC per decrescente, ASC (default) per crescente.

SELECT * FROM employees
ORDER BY salary DESC;

Output

Matteo Greco (75000) in cima, Sara Conti (39000) in fondo.

09 — GROUP BY — raggruppare righe

Raggruppa righe che condividono lo stesso valore, tipicamente insieme a una funzione di aggregazione.

SELECT department, COUNT(*)
FROM employees
GROUP BY department;

Output

department count(*)
HR 2
IT 4
Marketing 2
Sales 2

10 — HAVING — filtrare i gruppi

WHERE filtra le righe prima del raggruppamento; HAVING filtra i gruppi dopo l’aggregazione — è l’unico posto dove si può condizionare su COUNT() o SUM().

SELECT department, COUNT(*)
FROM employees
GROUP BY department
HAVING COUNT(*) > 2;

Output

Solo IT con 4 dipendenti: l’unico gruppo sopra soglia.

11 — DISTINCT — valori univoci

Elimina i duplicati dal risultato.

SELECT DISTINCT department FROM employees;

Output

HR, IT, Marketing, Sales — 4 righe, anche se i dipendenti totali sono 10.


03 — Limitare i risultati e unire le tabelle

12 — LIMIT / TOP — limitare i record

Restringe il numero di righe restituite (LIMIT in MySQL/PostgreSQL, TOP in SQL Server) — utilissimo insieme a ORDER BY.

SELECT * FROM employees LIMIT 5;

Output

Solo le prime 5 righe della tabella.

13 — INNER JOIN — solo i record corrispondenti

Restituisce le righe che hanno corrispondenza in entrambe le tabelle.

SELECT e.name, d.department_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.id;

Output

Tutti e 10 i dipendenti, ciascuno abbinato al nome esteso del proprio reparto (es. Anna Rossi → HR).

14 — LEFT JOIN — tutti i record di sinistra

Restituisce tutte le righe della tabella a sinistra (employees), anche se non hanno corrispondenza a destra.

SELECT e.name, d.department_name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.id;

Output

Identico all’INNER JOIN in questo dataset (nessun dipendente orfano). Se un dipendente avesse dept_id nullo, comparirebbe comunque con department_name = NULL.

15 — RIGHT JOIN — tutti i record di destra

Speculare al LEFT JOIN: restituisce tutte le righe della tabella a destra (departments), anche senza dipendenti associati.

SELECT e.name, d.department_name
FROM employees e
RIGHT JOIN departments d ON e.dept_id = d.id;

Output

Coincide con l’INNER JOIN qui, ma con un 5° reparto senza dipendenti (es. “Finance”) comparirebbe comunque, con name = NULL.


04 — Aggregare e interrogare in profondità

16 — COUNT / SUM / AVG

Le funzioni di aggregazione calcolano un valore riassuntivo su un insieme di righe.

SELECT
  COUNT(*) AS total_emp,
  SUM(salary) AS total_salary,
  AVG(salary) AS avg_salary
FROM employees;

Output

total_emp total_salary avg_salary
10 562000 56200

17 — Subquery — query annidata

Una query dentro un’altra query, utile per confronti dinamici che dipendono da un calcolo.

SELECT name
FROM employees
WHERE salary < (SELECT AVG(salary) FROM employees);

Output

Dipendenti sotto la media (56200): Anna Rossi, Sara Conti, Elena Ferrari, Chiara Marino.

18 — 2° stipendio più alto

Pattern classico da colloquio: combinare MAX e una subquery per escludere il valore più alto.

SELECT MAX(salary)
FROM employees
WHERE salary < (SELECT MAX(salary) FROM employees);

Output

71000 (Luca Neri) — il massimo assoluto è 75000 (Matteo Greco), escluso dalla subquery. In alternativa: ORDER BY salary DESC LIMIT 1 OFFSET 1.


05 — Modificare i dati

19 — DELETE — eliminare record

Rimuove righe che soddisfano una condizione. Attenzione: senza WHERE elimina l’intera tabella.

DELETE FROM employees WHERE id = 5;

Effetto

La riga di Sara Conti viene rimossa definitivamente dalla tabella.

20 — UPDATE — aggiornare record

Modifica il valore di una o più colonne per le righe che soddisfano la condizione.

UPDATE employees
SET salary = 60000
WHERE id = 1;

Effetto

Lo stipendio di Anna Rossi passa da 45000 a 60000.


Da ricordare

Punti chiave

  • WHERE filtra le righe — prima dell’aggregazione.
  • GROUP BY raggruppa righe con lo stesso valore.
  • HAVING filtra i gruppi — dopo l’aggregazione, non va confuso con WHERE.
  • JOIN combina dati provenienti da più tabelle.
  • ORDER BY ordina il risultato finale.
  • Le funzioni di aggregazione operano su gruppi o sull’intera tabella.
Forse potrebbe interessarti anche:  Network Science per il Marketing: La Guida Pratica per Campagne Virali con Dati e Python

Funzioni di aggregazione

Funzione Descrizione
COUNT() Numero di righe
SUM() Somma totale
AVG() Valore medio
MIN() Valore minimo
MAX() Valore massimo

Operatori logici

Operatore Descrizione
AND Entrambe le condizioni vere
OR Almeno una condizione vera
NOT Inverte il risultato
IN Valore presente in una lista
BETWEEN Valore in un intervallo
LIKE Pattern matching su testo

Prima del colloquio

Cinque consigli pratici

  1. Esercitati con query reali ogni giorno, non solo a memoria.
  2. Concentrati sulla logica del problema, non sulla sintassi esatta del dialetto.
  3. Pratica su dataset veri, non solo su tabelle di esempio semplificate.
  4. Approfondisci bene JOIN e subquery: sono il terreno delle domande più difficili.
  5. Scrivi query pulite e leggibili — indentazione e alias contano anche in colloquio.

Mettiti alla prova

Tre esercizi di verifica

Prova a scrivere la query prima di aprire la soluzione — usa sempre il dataset di esempio di questa guida.

1. Nome e stipendio dei dipendenti IT, dal più alto al più basso — mostra soluzione
SELECT name, salary
FROM employees
WHERE department = 'IT'
ORDER BY salary DESC;

Output atteso:

name salary
Luca Neri 71000
Davide Romano 67000
Marco Bianchi 62000
Giulia Verdi 58000
2. Reparti con stipendio medio sopra 50000 — mostra soluzione
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 50000;

Output atteso:

department avg_salary
IT 64500
Sales 65000

I valori medi sono calcolati sui dati di esempio.

3. Il 3° stipendio più alto in assoluto — mostra soluzione

Questo è in assoluto uno dei quesiti più frequenti e insidiosi nei colloqui per Data Analyst e Data Scientist. La vera difficoltà non risiede nella sintassi di base, ma nella gestione dei dati reali e dei parimerito.

Se due dipendenti condividono lo stipendio massimo (es. due manager a [math]75.000[/math]), un semplice ordinamento ORDER BY ... LIMIT 1 OFFSET 2 senza la parola chiave DISTINCT fallirebbe silenziosamente, prelevando il secondo stipendio più alto anziché il terzo gradino effettivo della scala retributiva.

Di seguito le soluzioni ottimali, divise per approccio e dialetto SQL.


Soluzione 1: Standard ed essenziale (MySQL, PostgreSQL, SQLite)

L’approccio più rapido e leggibile combina DISTINCT per accorpare i valori retributivi identici, ORDER BY per l’ordinamento decrescente e la coppia LIMIT / OFFSET per saltare i primi due valori e isolare il terzo.

SELECT DISTINCT salary
FROM employees
ORDER BY salary DESC
LIMIT 1 OFFSET 2;

Output atteso:

salary
67000

(Nota sul dataset: il 1° gradino è 75.000 di Matteo Greco, il 2° è 71.000 di Luca Neri, il 3° è 67.000 di Davide Romano).


Soluzione 2: Livello Senior con Window Functions (Consigliata nei colloqui)

Per ruoli Mid-Senior o Data Science, i selezionatori apprezzano molto di più l’utilizzo delle funzioni analitiche (Window Functions). Risolvere il problema con DENSE_RANK() dimostra una profonda maturità tecnica e rende la query facilmente scalabile (ad esempio, se il recruiter modificasse al volo la domanda in: “Ora trova il 3° stipendio più alto per ciascun reparto”).

Perché proprio DENSE_RANK()?

  • ROW_NUMBER() assegnerebbe numeri progressivi diversi anche a stipendi identici.
  • RANK() salterebbe la numerazione successiva in presenza di parimerito (es. dopo due primi posti, passerebbe direttamente al 3°).
  • DENSE_RANK(), invece, assegna lo stesso rango alle retribuzioni identiche e mantiene compattata la sequenza dei gradini successivi.
WITH RankedSalaries AS (
    SELECT 
        salary, 
        DENSE_RANK() OVER (ORDER BY salary DESC) AS rank_num
    FROM employees
)
SELECT DISTINCT salary 
FROM RankedSalaries 
WHERE rank_num = 3;

Output atteso:

salary
67000

Soluzione 3: Fallback per Database Legacy (Senza OFFSET)

Se l’intervistatore vuole testare la tua capacità di adattamento e ti chiede di scrivere la query per un motore di archiviazione legacy che non supporta la clausola OFFSET (come le vecchie versioni di SQL Server o Oracle), la strategia vincente consiste nell’isolare una “Top 3” in una subquery ed estrarne il valore minimo con MIN().

Forse potrebbe interessarti anche:  Carte di Controllo di Shewhart X̄-R: Guida Completa con Teoria, Python ed Esempi Pratici

Per SQL Server (T-SQL):

SELECT MIN(salary) AS third_highest_salary
FROM (
    SELECT DISTINCT TOP 3 salary
    FROM employees
    ORDER BY salary DESC
) AS top3;

Per Oracle (dalla versione 12c con FETCH FIRST):

SELECT MIN(salary) AS third_highest_salary
FROM (
    SELECT DISTINCT salary
    FROM employees
    ORDER BY salary DESC
    FETCH FIRST 3 ROWS ONLY
) AS top3;

Perché questi 3 esercizi sono perfetti per valutare un candidato?

Dal punto di vista dell’applicazione reale e della valutazione tecnica in sede di colloquio, i tre esercizi finali non sono scelti a caso: tracciano un percorso di difficoltà crescente che mappa esattamente i tre livelli di competenza logica del candidato (Junior, Mid, Senior/Ready-for-production).

1. L’Esercizio 1: La verifica dell’ordine di esecuzione

A prima vista è una query elementare, ma nel live coding serve a verificare una competenza fondamentale: la comprensione dell’ordine cronologico in cui il motore del database processa le istruzioni. Molti candidati alle prime armi tentano di usare un alias di colonna o applicano l’ordinamento prima di aver ridotto lo spazio di ricerca con la clausola WHERE. In un contesto applicativo di Big Data, filtrare prima di ordinare è la base dell’ottimizzazione dei costi e dei tempi di esecuzione (riduzione del dataset scanning prima dell’operazione costosa di sorting).

2. L’Esercizio 2: Il trabocchetto dell’aggregazione condizionale

Questo esercizio rappresenta il classico spartiacque nei colloqui per Data Analyst. La sua peculiarità applicativa risiede nell’obbligare il candidato a distinguere tra un filtro a livello di riga grezza (WHERE) e un filtro a livello di entità aggregata (HAVING).

Nel mondo reale della business intelligence, fare questo errore significa generare KPI errati: ad esempio, se per sbaglio si provasse a filtrare con WHERE salary > 50000 prima di fare la media per reparto, si otterrebbe la “media degli stipendi alti per reparto” e non “i reparti che hanno una media stipendiale alta”, stravolgendo completamente il significato logico dell’analisi per il management.

3. L’Esercizio 3: La gestione delle anomalie nei dati (Il test sulla difensività)

Chiedere l’N-esimo elemento di una classifica è probabilmente la domanda più inflazionata nella storia dei colloqui SQL, ma è il modo in cui il candidato la risolve che fa la differenza.

L’esercizio è estremamente interessante dal punto di vista applicativo perché forza a ragionare come un Data Engineer:

cosa succede se i dati reali non sono ideali?

Se due dipendenti condividono lo stesso stipendio massimo (es. due dirigenti a 75.000€), un semplice ORDER BY ... LIMIT 1 OFFSET 2 senza la parola chiave DISTINCT fallirebbe silenziosamente, restituendo il secondo valore massimo anziché il terzo gradino reale della scala retributiva.

Questa domanda permette all’intervistatore di aprire una discussione architetturale di altissimo valore sulle prestazioni: su tabelle da milioni di righe, quanto costa una subquery rispetto a una Window Function come DENSE_RANK()? Saper discutere i trade-off tra leggibilità del codice, portabilità tra dialetti SQL diversi e complessità computazionale è il biglietto da visita che trasforma una buona intervista tecnico-sintattica in un’assunzione sicura.

 

Pubblicità