🧚 Ascolta! Beekeeper Studio è una GUI per database veloce, moderna e open source Scarica
7 gennaio 2026 Di Lucas Gray

Una subquery è una query annidata dentro un'altra query. Una CTE (common table expression) è la stessa query interna spostata in cima allo statement con WITH e a cui viene dato un nome, così una query lunga si legge come una serie di passaggi. Gli esempi funzionano in PostgreSQL, MySQL, SQLite e SQL Server.

Come usare CTE e subquery

Una subquery si scrive dove viene usata: in FROM fa da tabella, mentre in WHERE o SELECT fornisce un valore o una lista. Una CTE si definisce prima e poi la query che segue la usa come una tabella:

-- Subquery: written where it's used
SELECT columns FROM (SELECT ...) AS name;

-- CTE: named first, then used like a table
WITH name AS (SELECT ...)
SELECT columns FROM name;
SELECT n * 2 AS doubled FROM (SELECT 21 AS n) AS numbers;
-- Output: 42

WITH numbers AS (SELECT 21 AS n)
SELECT n * 2 AS doubled FROM numbers;
-- Output: 42

Su una tabella reale le due forme restituiscono le stesse righe. Ecco lo stesso filtro scritto in entrambi i modi:

-- Input: people
-- first_name | last_name | job      | favorite_food
-- -----------+-----------+----------+--------------
-- Matthew    | Ortiz     | Chef     | cheese
-- Matt       | Novak     | Pilot    | pizza
-- Mattie     | Brooks    | Engineer | cheese
-- Priya      | Nair      | Teacher  | cheese

-- Subquery
SELECT *
FROM (SELECT first_name, last_name, job FROM people WHERE favorite_food = 'cheese') AS cheese_lovers
WHERE first_name LIKE 'Matt%';

-- CTE
WITH cheese_lovers AS (
  SELECT first_name, last_name, job FROM people WHERE favorite_food = 'cheese'
)
SELECT * FROM cheese_lovers
WHERE first_name LIKE 'Matt%';

-- Output of both:
-- first_name | last_name | job
-- -----------+-----------+---------
-- Matthew    | Ortiz     | Chef
-- Mattie     | Brooks    | Engineer

Regole da conoscere

  • Una CTE vale per un solo statement. La query subito dopo WITH può usarla; lo statement successivo no. Per conservare un risultato per più query, usa una tabella temporanea o una vista.
  • Dai un nome a una subquery in FROM, come AS numbers qui sopra. MySQL e SQL Server la rifiutano se manca.
  • Una subquery usata come valore singolo deve restituire una sola riga:
SELECT (SELECT 1 UNION ALL SELECT 2) AS n;
-- ERROR: more than one row returned by a subquery used as an expression
-- (SQLite doesn't complain: it quietly uses the first row, 1)
  • Di solito la velocità è la stessa. In genere i database pianificano una CTE usata una sola volta come la subquery equivalente, quindi scegli quella più leggibile.

Sintassi alternativa

Una CTE che fa riferimento a se stessa (vedi sotto) si scrive WITH RECURSIVE:

-- In SQL Server, leave out RECURSIVE
WITH RECURSIVE counter AS (
  SELECT 1 AS n
  UNION ALL
  SELECT n + 1 FROM counter WHERE n < 3
)
SELECT n FROM counter;

-- Output:
-- n
-- -
-- 1
-- 2
-- 3

In SQL Server, lo statement prima di un WITH deve terminare con un punto e virgola: ecco perché nel codice T-SQL una CTE spesso inizia con ;WITH.

Quando usare una CTE invece di una subquery?

Una subquery va bene per un singolo piccolo passaggio. Passa a una CTE quando la query ha più passaggi, le serve lo stesso risultato due volte o deve fare riferimento a se stessa.

Filtrare rispetto a un valore calcolato

Quando la query interna produce solo un valore o una lista con cui confrontare, come il totale medio degli ordini, una subquery in WHERE è il modo più breve per scriverla. Funzionerebbe anche una CTE, ma aggiungerebbe un nome che non serve a nessuno.

Dividere una query lunga in passaggi con un nome

Quando una query ha più fasi (totale per cliente, poi i primi della lista, poi l'elenco finale), dai un nome a ogni fase. Elenca più CTE dopo un solo WITH, separate da virgole: ognuna può usare quelle precedenti, e la query si legge dall'alto in basso invece che dall'interno verso l'esterno.

Usare lo stesso risultato più volte

Uno statement può fare riferimento a una CTE tutte le volte che vuole, quindi un risultato che ti serve due volte (una lista di totali e la loro media, o ogni totale e il totale complessivo) si scrive una volta sola. Con le subquery dovresti copiare l'intera query interna.

Percorrere una gerarchia

Solo una CTE può fare riferimento a se stessa. Una CTE ricorsiva parte da alcune righe e continua a fare join con la tabella finché non compaiono più righe nuove: è così che percorri un organigramma o un albero di categorie, o generi una serie di numeri o date.

Esempi reali di CTE e subquery

Prodotti con prezzo superiore alla media della categoria

La subquery fa riferimento a p.category della query esterna, quindi viene calcolata per la categoria di ciascun prodotto (una subquery correlata):

-- Input: products
-- name          | category  | price
-- --------------+-----------+-------
-- Desk Lamp     | Lighting  | 24.99
-- Floor Lamp    | Lighting  | 89.00
-- Office Chair  | Furniture | 149.00
-- Standing Desk | Furniture | 399.00
-- Bookshelf     | Furniture | 120.00

SELECT name, category, price
FROM products p
WHERE price > (
  SELECT AVG(price) FROM products WHERE category = p.category
)
ORDER BY category, name;

-- Output:
-- name          | category  | price
-- --------------+-----------+-------
-- Standing Desk | Furniture | 399.00
-- Floor Lamp    | Lighting  | 89.00

Il prodotto più venduto di ogni categoria

Tre passaggi, ognuno con un nome: somma le unità per prodotto, ordina i prodotti all'interno di ogni categoria, poi tieni il primo:

-- Input: sales
-- id | product      | category  | units
-- ---+--------------+-----------+------
-- 1  | Desk Lamp    | Lighting  | 3
-- 2  | Floor Lamp   | Lighting  | 5
-- 3  | Desk Lamp    | Lighting  | 4
-- 4  | Office Chair | Furniture | 2
-- 5  | Bookshelf    | Furniture | 6

WITH product_totals AS (
  SELECT category, product, SUM(units) AS units
  FROM sales
  GROUP BY category, product
),
ranked AS (
  SELECT category, product, units,
         RANK() OVER (PARTITION BY category ORDER BY units DESC) AS place
  FROM product_totals
)
SELECT category, product, units
FROM ranked
WHERE place = 1
ORDER BY category;

-- Output:
-- category  | product   | units
-- ----------+-----------+------
-- Furniture | Bookshelf | 6
-- Lighting  | Desk Lamp | 7

La quota di vendite di ogni regione

region_totals viene usata due volte: una per le righe e una dentro una subquery per il totale complessivo:

-- Input: orders
-- id | region | amount
-- ---+--------+--------
-- 1  | North  | 1200.00
-- 2  | South  | 450.00
-- 3  | North  | 300.00
-- 4  | West   | 800.00
-- 5  | South  | 250.00

WITH region_totals AS (
  SELECT region, SUM(amount) AS total
  FROM orders
  GROUP BY region
)
SELECT
  region,
  total,
  ROUND(100.0 * total / (SELECT SUM(total) FROM region_totals), 1) AS pct_of_sales
FROM region_totals
ORDER BY total DESC;

-- Output:
-- region | total   | pct_of_sales
-- -------+---------+-------------
-- North  | 1500.00 | 50.0
-- West   | 800.00  | 26.7
-- South  | 700.00  | 23.3

Scritta con le subquery, la query con GROUP BY comparirebbe due volte.

La catena di manager di un dipendente

Parti da un dipendente e segui manager_id verso l'alto finché non si esaurisce:

-- Input: employees
-- id | name      | manager_id
-- ---+-----------+-----------
-- 1  | Maya Chen | NULL
-- 2  | Sam Ortiz | 1
-- 3  | Jo Kim    | 2
-- 4  | Lee Ross  | 2
-- 5  | Ana Diaz  | 1

-- In SQL Server, leave out RECURSIVE
WITH RECURSIVE chain AS (
  SELECT id, name, manager_id, 0 AS level
  FROM employees
  WHERE name = 'Jo Kim'
  UNION ALL
  SELECT e.id, e.name, e.manager_id, chain.level + 1
  FROM employees e
  JOIN chain ON e.id = chain.manager_id
)
SELECT level, name FROM chain ORDER BY level;

-- Output:
-- level | name
-- ------+----------
-- 0     | Jo Kim
-- 1     | Sam Ortiz
-- 2     | Maya Chen

Altre letture su CTE e subquery

Su questo blog:

Documentazione ufficiale:

Beekeeper Studio è una GUI per database gratuita & open source

Il miglior strumento per query & editor SQL che abbia mai usato. Offre tutto ciò che mi serve per gestire il mio database. - ⭐⭐⭐⭐⭐ Mit

Beekeeper Studio è veloce, intuitivo e facile da usare. Beekeeper supporta tantissimi database e funziona alla grande su Windows, Mac e Linux.

La versione Linux di Beekeeper è completa al 100%, senza tagli e senza compromessi sulle funzionalità.

Cosa dicono gli utenti di Beekeeper Studio

★★★★★
"Beekeeper Studio ha sostituito completamente il mio vecchio workflow SQL. È veloce, intuitivo e rende di nuovo piacevole lavorare con i database."
— Alex K., Database Developer
★★★★★
"Ho provato molte GUI per database, ma Beekeeper trova il perfetto equilibrio tra funzionalità e semplicità. Funziona e basta."
— Sarah M., Full Stack Engineer

Pronto a migliorare il tuo workflow SQL?

download Scarica gratis