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

Una relazione uno a molti collega una riga di una tabella padre a un numero qualsiasi di righe di una tabella figlia, come un cliente e i suoi ordini. La costruisci con una colonna foreign key nella tabella figlia che punta al padre. Gli esempi funzionano in PostgreSQL, MySQL, SQLite e SQL Server.

Come modellare una relazione uno a molti

Dai alla tabella figlia, il lato “molti”, una colonna che contiene la primary key del padre e dichiarala come foreign key. Ogni ordine memorizza l’id del cliente che l’ha effettuato, e la tabella customers non cambia affatto.

Diagramma di una relazione uno a molti in un database

CREATE TABLE customers (
    id INTEGER PRIMARY KEY,
    name VARCHAR(100) NOT NULL
);

-- Each order stores the id of the customer who placed it
CREATE TABLE orders (
    id INTEGER PRIMARY KEY,
    customer_id INTEGER NOT NULL,
    order_date DATE NOT NULL,
    total DECIMAL(10,2) NOT NULL,
    FOREIGN KEY (customer_id) REFERENCES customers(id)
);

Unisci le due tabelle sulla foreign key per vedere ogni cliente con i suoi ordini:

-- Input: customers
-- id | name
-- ---+------
-- 1  | Alice
-- 2  | Bob
-- 3  | Carol

-- Input: orders
-- id | customer_id | order_date | total
-- ---+-------------+------------+-------
-- 1  | 2           | 2026-03-02 | 42.50
-- 2  | 1           | 2026-03-05 | 18.00
-- 3  | 2           | 2026-04-11 | 120.00
-- 4  | 2           | 2026-05-20 | 64.99

SELECT c.name, o.order_date, o.total
FROM customers c
JOIN orders o ON c.id = o.customer_id
ORDER BY c.name, o.order_date;

-- Output:
-- name  | order_date | total
-- ------+------------+-------
-- Alice | 2026-03-05 | 18.00
-- Bob   | 2026-03-02 | 42.50
-- Bob   | 2026-04-11 | 120.00
-- Bob   | 2026-05-20 | 64.99

Bob ha tre ordini e ogni ordine ha un solo cliente.

Regole da conoscere

  • La foreign key va nella tabella figlia. Ogni ordine registra il proprio cliente. Non dare mai a customers una colonna che elenca gli ID degli ordini.
  • Un semplice JOIN esclude i padri senza figli. Carol non compare nel risultato qui sopra. Usa un LEFT JOIN quando ogni padre deve comparire, come negli esempi più avanti.
  • Indicizza la foreign key. Join e ricerche per cliente richiedono un indice su orders.customer_id (MySQL ne crea uno per te insieme alla foreign key):
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
  • La foreign key protegge entrambe le tabelle. Rifiuta un ordine per un cliente che non esiste e, per impostazione predefinita, impedisce di eliminare un cliente che ha ancora ordini. Aggiungi ON DELETE CASCADE per eliminare i suoi ordini insieme a lui, oppure ON DELETE SET NULL per mantenere gli ordini senza cliente (la colonna deve quindi ammettere NULL):
-- Input: the customers and orders tables above

INSERT INTO orders VALUES (5, 99, '2026-06-01', 10.00);
-- ERROR: insert or update on table "orders" violates foreign key constraint "orders_customer_id_fkey"

DELETE FROM customers WHERE id = 1;
-- ERROR: update or delete on table "customers" violates foreign key constraint "orders_customer_id_fkey" on table "orders"

-- With FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE CASCADE
DELETE FROM customers WHERE id = 1;

-- Output (orders after the DELETE):
-- id | customer_id | order_date | total
-- ---+-------------+------------+-------
-- 1  | 2           | 2026-03-02 | 42.50
-- 3  | 2           | 2026-04-11 | 120.00
-- 4  | 2           | 2026-05-20 | 64.99
  • SQLite ignora le foreign key finché non le attivi. Esegui PRAGMA foreign_keys = ON; su ogni connessione, altrimenti SQLite accetta ordini per clienti che non esistono.

Sintassi alternativa

Ci sono altri tre modi per dichiarare la stessa chiave:

-- Inline on the column (MySQL 8.0 accepts this but silently ignores it)
customer_id INTEGER NOT NULL REFERENCES customers(id)

-- Named, so errors and later changes can refer to it
CONSTRAINT fk_orders_customer FOREIGN KEY (customer_id) REFERENCES customers(id)

-- Added to an existing table (SQLite can't add a foreign key this way)
ALTER TABLE orders ADD CONSTRAINT fk_orders_customer
    FOREIGN KEY (customer_id) REFERENCES customers(id);

Per gli id generati dal database, dichiara la chiave del padre come SERIAL PRIMARY KEY in PostgreSQL, INT AUTO_INCREMENT PRIMARY KEY in MySQL, INTEGER PRIMARY KEY in SQLite o INT IDENTITY PRIMARY KEY in SQL Server. La colonna foreign key resta un semplice intero.

Quando usare una relazione uno a molti?

Usala ogni volta che ogni figlio appartiene esattamente a un padre: clienti e ordini, post e commenti, reparti e dipendenti, categorie e prodotti. Se un figlio può avere più padri, come uno studente che segue più corsi, ti serve invece una relazione molti a molti. Ecco i problemi che una relazione uno a molti ti permette di risolvere.

Mostrare ogni padre con i suoi figli

Una fattura con le sue righe, un post con i suoi commenti: unisci la tabella figlia al padre sulla foreign key. Le colonne del padre si ripetono su ogni riga figlia.

Contare i figli di ogni padre

Quanti ordini ha effettuato ogni cliente, quanti articoli ha scritto ogni autore: LEFT JOIN, raggruppa per padre e conta una colonna del figlio. Contare una colonna del figlio invece di * fa risultare 0 per i padri senza figli.

Trovare i padri senza figli

Clienti che non hanno mai ordinato, reparti senza progetti, prodotti che nessuno ha recensito. Un LEFT JOIN riempie le colonne del figlio con NULL quando non c’è corrispondenza, quindi tieni le righe in cui l’id del figlio è NULL.

Sommare i valori dei figli

Fatturato per cliente, valore di magazzino per categoria, ore registrate per progetto: SUM, AVG, MIN o MAX sulle righe figlie, raggruppate per padre. Un padre senza figli ottiene NULL, che COALESCE trasforma in 0.

Memorizzare una gerarchia in una sola tabella

Dipendenti e i loro manager, cartelle e sottocartelle, categorie e sottocategorie: la foreign key punta alla stessa tabella e un NULL indica la cima dell’albero. Unisci la tabella a sé stessa per affiancare ogni riga al suo padre.

Esempi reali di relazioni uno a molti

Mostrare l’ultimo ordine di ogni cliente

ROW_NUMBER() numera gli ordini di ogni cliente dal più recente al più vecchio. Tieni il numero 1:

-- Input: customers
-- id | name
-- ---+------
-- 1  | Alice
-- 2  | Bob
-- 3  | Carol

-- Input: orders
-- id | customer_id | order_date | total
-- ---+-------------+------------+-------
-- 1  | 2           | 2026-03-02 | 42.50
-- 2  | 1           | 2026-03-05 | 18.00
-- 3  | 2           | 2026-04-11 | 120.00
-- 4  | 2           | 2026-05-20 | 64.99

SELECT name, order_date, total
FROM (
    SELECT c.name, o.order_date, o.total,
           ROW_NUMBER() OVER (PARTITION BY c.id ORDER BY o.order_date DESC) AS rn
    FROM customers c
    JOIN orders o ON c.id = o.customer_id
) latest
WHERE rn = 1
ORDER BY name;

-- Output:
-- name  | order_date | total
-- ------+------------+------
-- Alice | 2026-03-05 | 18.00
-- Bob   | 2026-05-20 | 64.99

Classificare i clienti per numero di ordini

-- Input: the customers and orders tables above

SELECT c.name, COUNT(o.id) AS order_count
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.name
ORDER BY order_count DESC;

-- Output:
-- name  | order_count
-- ------+------------
-- Bob   | 3
-- Alice | 1
-- Carol | 0

Trovare i clienti che non hanno mai ordinato

-- Input: the customers and orders tables above

SELECT c.name
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
WHERE o.id IS NULL;

-- Output:
-- name
-- -----
-- Carol

NOT EXISTS fa lo stesso lavoro e si legge in modo più vicino alla domanda:

-- Input: the customers and orders tables above

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

-- Output:
-- name
-- -----
-- Carol

Trovare i clienti che spendono di più

SUM su nessuna riga dà NULL, quindi COALESCE trasforma il totale di Carol in 0:

-- Input: the customers and orders tables above

SELECT c.name, COALESCE(SUM(o.total), 0) AS total_spent
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.name
ORDER BY total_spent DESC;

-- Output:
-- name  | total_spent
-- ------+------------
-- Bob   | 227.49
-- Alice | 18.00
-- Carol | 0

Percorrere un organigramma dall’alto verso il basso

Il manager_id di ogni dipendente punta a un altro dipendente. Una query ricorsiva parte dalla persona senza manager e aggiunge un livello di sottoposti alla volta:

CREATE TABLE employees (
    id INTEGER PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    manager_id INTEGER,  -- NULL for the person at the top
    FOREIGN KEY (manager_id) REFERENCES employees(id)
);

-- Input: employees
-- id | name  | manager_id
-- ---+-------+-----------
-- 1  | Maya  | NULL
-- 2  | Raj   | 1
-- 3  | Lena  | 1
-- 4  | Tom   | 2
-- 5  | Sofia | 2

-- In SQL Server, write WITH instead of WITH RECURSIVE
WITH RECURSIVE org_chart AS (
    -- Start at the top: the employee with no manager
    SELECT id, name, 0 AS level
    FROM employees
    WHERE manager_id IS NULL

    UNION ALL

    -- Then add each employee's direct reports, one level at a time
    SELECT e.id, e.name, oc.level + 1
    FROM employees e
    JOIN org_chart oc ON e.manager_id = oc.id
)
SELECT name, level
FROM org_chart
ORDER BY level, name;

-- Output:
-- name  | level
-- ------+------
-- Maya  | 0
-- Lena  | 1
-- Raj   | 1
-- Sofia | 2
-- Tom   | 2

Approfondimenti sulle relazioni uno a molti

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 flusso di lavoro 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 l'equilibrio perfetto tra funzionalità e semplicità. Funziona e basta."
— Sarah M., Full Stack Engineer

Pronto a migliorare il tuo flusso di lavoro SQL?

download Scarica gratis