Introduzione
In questa lezione finale approfondiremo le operazioni di modifica dei dati in MySQL e le query avanzate per interrogare database complessi.
Dopo aver imparato a creare database e tabelle nella Lezione 3, ora è il momento di imparare come inserire, modificare ed eliminare i dati, oltre a eseguire query più sofisticate con JOIN, filtri avanzati e aggregazioni.
Obiettivi della Lezione
- Eseguire query SELECT avanzate con filtri e ordinamenti
- Comprendere e utilizzare i JOIN tra tabelle
- Inserire nuovi dati con INSERT e REPLACE
- Modificare record esistenti con UPDATE
- Eliminare dati con DELETE e TRUNCATE
- Importare dati in massa con LOAD DATA
- Ottimizzare le query per migliorare le performance
Query SELECT Avanzate
Struttura Base della SELECT
La sintassi completa di una query SELECT include molte clausole opzionali che permettono di filtrare, ordinare e aggregare i dati:
SELECT colonna1, colonna2, ...
FROM nome_tabella
WHERE condizione
GROUP BY colonna
HAVING condizione_gruppo
ORDER BY colonna ASC|DESC
LIMIT numero;
Filtri con WHERE
La clausola WHERE permette di filtrare i risultati in base a condizioni specifiche:
-- Filtro semplice
SELECT * FROM studenti
WHERE eta > 18;
-- Filtri multipli con AND/OR
SELECT * FROM studenti
WHERE eta > 18 AND corso = 'Informatica';
-- Filtro con LIKE (pattern matching)
SELECT * FROM studenti
WHERE nome LIKE 'Mar%'; -- Nomi che iniziano con "Mar"
-- Filtro con IN (lista di valori)
SELECT * FROM studenti
WHERE citta IN ('Roma', 'Milano', 'Firenze');
-- Filtro con BETWEEN (intervallo)
SELECT * FROM studenti
WHERE eta BETWEEN 20 AND 25;
Ordinamento con ORDER BY
ORDER BY ordina i risultati in modo crescente (ASC) o decrescente (DESC):
-- Ordinamento crescente
SELECT * FROM studenti
ORDER BY cognome ASC;
-- Ordinamento decrescente
SELECT * FROM studenti
ORDER BY eta DESC;
-- Ordinamento multiplo
SELECT * FROM studenti
ORDER BY corso ASC, eta DESC;
Limitare i Risultati con LIMIT
LIMIT restringe il numero di risultati restituiti:
-- Primi 10 risultati
SELECT * FROM studenti
LIMIT 10;
-- Risultati dalla posizione 20, ne restituisce 10
SELECT * FROM studenti
LIMIT 20, 10;
Funzioni di Aggregazione
Le funzioni di aggregazione calcolano valori su insiemi di righe:
-- Contare il numero di studenti
SELECT COUNT(*) FROM studenti;
-- Età media
SELECT AVG(eta) AS eta_media FROM studenti;
-- Età minima e massima
SELECT MIN(eta) AS eta_minima, MAX(eta) AS eta_massima
FROM studenti;
-- Somma totale
SELECT SUM(crediti) AS crediti_totali FROM esami;
Raggruppamento con GROUP BY
GROUP BY raggruppa i risultati per valori comuni:
-- Contare studenti per corso
SELECT corso, COUNT(*) AS numero_studenti
FROM studenti
GROUP BY corso;
-- Età media per corso
SELECT corso, AVG(eta) AS eta_media
FROM studenti
GROUP BY corso;
-- Filtrare gruppi con HAVING
SELECT corso, COUNT(*) AS numero_studenti
FROM studenti
GROUP BY corso
HAVING COUNT(*) > 10; -- Solo corsi con più di 10 studenti
Differenza tra WHERE e HAVING
WHERE filtra le righe prima del raggruppamento, HAVING filtra i gruppi dopo il raggruppamento. HAVING si usa sempre con GROUP BY e può contenere funzioni di aggregazione.
JOIN: Unire Dati da Più Tabelle
I JOIN permettono di combinare dati provenienti da più tabelle in base a una relazione comune.
INNER JOIN
Restituisce solo le righe che hanno corrispondenza in entrambe le tabelle:
-- JOIN tra studenti ed esami
SELECT studenti.nome, studenti.cognome, esami.materia, esami.voto
FROM studenti
INNER JOIN esami ON studenti.id = esami.studente_id;
-- Con alias per semplificare
SELECT s.nome, s.cognome, e.materia, e.voto
FROM studenti AS s
INNER JOIN esami AS e ON s.id = e.studente_id;
LEFT JOIN
Restituisce tutte le righe della tabella di sinistra, anche se non hanno corrispondenza nella tabella di destra:
-- Tutti gli studenti, anche quelli senza esami
SELECT s.nome, s.cognome, e.materia, e.voto
FROM studenti AS s
LEFT JOIN esami AS e ON s.id = e.studente_id;
RIGHT JOIN
Restituisce tutte le righe della tabella di destra, anche se non hanno corrispondenza nella tabella di sinistra:
-- Tutti gli esami, anche quelli senza studente associato
SELECT s.nome, s.cognome, e.materia, e.voto
FROM studenti AS s
RIGHT JOIN esami AS e ON s.id = e.studente_id;
JOIN Multipli
È possibile eseguire JOIN su più di due tabelle:
-- JOIN tra studenti, esami e corsi
SELECT s.nome, s.cognome, e.materia, e.voto, c.nome_corso
FROM studenti AS s
INNER JOIN esami AS e ON s.id = e.studente_id
INNER JOIN corsi AS c ON e.corso_id = c.id;
Best Practice per i JOIN
- Usa sempre alias per rendere le query più leggibili
- Specifica sempre le colonne da selezionare (evita SELECT *)
- Assicurati che le colonne usate nel JOIN abbiano indici per migliorare le performance
- Usa INNER JOIN quando vuoi solo le righe con corrispondenza in entrambe le tabelle
- Usa LEFT JOIN quando vuoi tutte le righe della tabella principale
INSERT: Inserire Nuovi Dati
Sintassi Base
Il comando INSERT aggiunge nuove righe a una tabella:
-- Inserimento di una singola riga
INSERT INTO studenti (nome, cognome, eta, corso)
VALUES ('Mario', 'Rossi', 22, 'Informatica');
-- Inserimento senza specificare le colonne (devi fornire tutti i valori)
INSERT INTO studenti
VALUES (1, 'Mario', 'Rossi', 22, 'Informatica');
Inserimento Multiplo
È possibile inserire più righe contemporaneamente:
-- Inserire più studenti con una sola query
INSERT INTO studenti (nome, cognome, eta, corso)
VALUES
('Mario', 'Rossi', 22, 'Informatica'),
('Laura', 'Bianchi', 21, 'Matematica'),
('Giuseppe', 'Verdi', 23, 'Fisica');
INSERT con SELECT
Puoi inserire dati provenienti da un'altra tabella:
-- Copiare studenti da una tabella temporanea
INSERT INTO studenti (nome, cognome, eta, corso)
SELECT nome, cognome, eta, corso
FROM studenti_temp
WHERE anno = 2025;
REPLACE
REPLACE funziona come INSERT, ma se esiste già una riga con la stessa chiave primaria, la elimina e inserisce la nuova:
-- Se esiste uno studente con id=1, lo sostituisce
REPLACE INTO studenti (id, nome, cognome, eta, corso)
VALUES (1, 'Mario', 'Rossi', 23, 'Informatica');
Attenzione con REPLACE
REPLACE elimina completamente la riga esistente e ne inserisce una nuova. Questo può causare la perdita di dati se non specifichi tutte le colonne. Preferisci usare INSERT ... ON DUPLICATE KEY UPDATE per aggiornamenti più sicuri.
UPDATE: Modificare Dati Esistenti
Sintassi Base
Il comando UPDATE modifica i dati esistenti in una tabella:
-- Aggiornare una singola colonna
UPDATE studenti
SET eta = 23
WHERE id = 1;
-- Aggiornare più colonne contemporaneamente
UPDATE studenti
SET eta = 23, corso = 'Ingegneria Informatica'
WHERE id = 1;
UPDATE con Condizioni Complesse
Puoi usare condizioni complesse per aggiornare più righe:
-- Aggiornare tutti gli studenti di un corso
UPDATE studenti
SET anno = 2
WHERE corso = 'Informatica' AND eta > 20;
-- Aumentare l'età di tutti gli studenti di 1 anno
UPDATE studenti
SET eta = eta + 1;
UPDATE con JOIN
È possibile aggiornare dati usando informazioni da altre tabelle:
-- Aggiornare il campo "voto_medio" nella tabella studenti
UPDATE studenti AS s
INNER JOIN (
SELECT studente_id, AVG(voto) AS media
FROM esami
GROUP BY studente_id
) AS e ON s.id = e.studente_id
SET s.voto_medio = e.media;
Attenzione: Usa Sempre WHERE!
Se ometti la clausola WHERE in un UPDATE, tutte le righe della tabella verranno aggiornate! Questo può portare a perdite di dati irreversibili. Verifica sempre le condizioni prima di eseguire un UPDATE.
DELETE: Eliminare Dati
Sintassi Base
Il comando DELETE rimuove righe da una tabella:
-- Eliminare una singola riga
DELETE FROM studenti
WHERE id = 1;
-- Eliminare più righe con condizioni
DELETE FROM studenti
WHERE eta < 18;
-- Eliminare tutti gli studenti di un corso
DELETE FROM studenti
WHERE corso = 'Fisica';
DELETE con JOIN
Puoi eliminare righe usando condizioni provenienti da altre tabelle:
-- Eliminare studenti senza esami
DELETE s
FROM studenti AS s
LEFT JOIN esami AS e ON s.id = e.studente_id
WHERE e.studente_id IS NULL;
PERICOLO: DELETE Senza WHERE
Un DELETE senza WHERE eliminerà TUTTE le righe della tabella! Questa operazione è irreversibile se non hai un backup. Usa sempre la clausola WHERE e verifica la query con una SELECT prima di eseguire il DELETE.
Altri Comandi Utili
TRUNCATE
TRUNCATE svuota completamente una tabella, ma è più veloce di DELETE perché non registra ogni singola riga eliminata:
-- Svuotare completamente la tabella studenti
TRUNCATE TABLE studenti;
-- Equivalente a DELETE FROM studenti, ma più veloce
Differenze tra DELETE e TRUNCATE
- DELETE: Può usare WHERE per eliminare righe specifiche. Più lento ma più flessibile.
- TRUNCATE: Elimina tutte le righe. Più veloce ma non può usare WHERE. Resetta anche i contatori AUTO_INCREMENT.
LOAD DATA
LOAD DATA permette di importare grandi quantità di dati da un file (CSV, TXT, ecc.):
-- Importare dati da un file CSV
LOAD DATA INFILE '/percorso/studenti.csv'
INTO TABLE studenti
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS; -- Salta la prima riga (intestazioni)
Esempio di file CSV da importare:
nome,cognome,eta,corso
Mario,Rossi,22,Informatica
Laura,Bianchi,21,Matematica
Giuseppe,Verdi,23,Fisica
Creare Indici per Ottimizzare le Query
Gli indici migliorano notevolmente le performance delle query, specialmente su tabelle grandi:
-- Creare un indice su una singola colonna
CREATE INDEX idx_cognome ON studenti(cognome);
-- Indice su più colonne
CREATE INDEX idx_corso_eta ON studenti(corso, eta);
-- Indice unico (valori univoci)
CREATE UNIQUE INDEX idx_email ON studenti(email);
-- Eliminare un indice
DROP INDEX idx_cognome ON studenti;
Quando Usare gli Indici
- Su colonne usate frequentemente in WHERE, JOIN e ORDER BY
- Su colonne con alta cardinalità (molti valori diversi)
- Su chiavi esterne per migliorare i JOIN
- NON su tabelle molto piccole (< 1000 righe)
- NON su colonne che cambiano spesso (rallentano INSERT/UPDATE)
Grafici e Visualizzazione Dati con MySQL
MySQL non genera grafici direttamente, ma fornisce i dati che possono essere visualizzati tramite strumenti esterni come Excel, Google Sheets, Power BI, Tableau, o librerie di programmazione.
Preparare Dati per i Grafici
Per creare grafici efficaci, è essenziale strutturare i dati in modo appropriato con query aggregate:
-- Dati per grafico a barre: Vendite per categoria
SELECT categoria, SUM(quantita) AS totale_vendite
FROM prodotti
GROUP BY categoria
ORDER BY totale_vendite DESC;
-- Dati per grafico a linea: Trend vendite mensili
SELECT
YEAR(data_ordine) AS anno,
MONTH(data_ordine) AS mese,
SUM(totale) AS vendite_mensili
FROM ordini
GROUP BY anno, mese
ORDER BY anno, mese;
-- Dati per grafico a torta: Distribuzione clienti per città
SELECT citta, COUNT(*) AS numero_clienti
FROM clienti
GROUP BY citta;
Tipi di Grafici e Query Corrispondenti
Grafico a Barre / Istogramma
Utilizzo: Confrontare valori tra diverse categorie.
Query tipo: SELECT categoria, COUNT(*) o SUM() con GROUP BY
-- Esempio: Numero di ordini per stato
SELECT stato_ordine, COUNT(*) AS numero_ordini
FROM ordini
GROUP BY stato_ordine
ORDER BY numero_ordini DESC;
Grafico a Linea
Utilizzo: Visualizzare trend nel tempo (serie temporali).
Query tipo: SELECT con funzioni data (DATE, YEAR, MONTH) e GROUP BY temporale
-- Esempio: Trend registrazioni utenti per giorno
SELECT
DATE(data_registrazione) AS giorno,
COUNT(*) AS nuovi_utenti
FROM utenti
WHERE data_registrazione >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
GROUP BY giorno
ORDER BY giorno;
Grafico a Torta
Utilizzo: Mostrare proporzioni di un intero (percentuali).
Query tipo: SELECT con COUNT(*) o SUM() diviso per il totale
-- Esempio: Percentuale di prodotti per categoria
SELECT
categoria,
COUNT(*) AS quantita,
ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM prodotti), 2) AS percentuale
FROM prodotti
GROUP BY categoria;
Esportare Dati per Grafici
Esistono diversi metodi per esportare i risultati delle query e crearci sopra dei grafici:
Metodo 1: Esportazione CSV per Excel/Google Sheets
Puoi esportare i risultati in formato CSV usando phpMyAdmin o MySQL Workbench:
-- Esportare risultati in CSV (da riga di comando)
SELECT * FROM vendite_mensili
INTO OUTFILE '/tmp/vendite.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';
Metodo 2: Connessione Diretta con Python/JavaScript
Usa librerie di visualizzazione come Chart.js, Plotly, o matplotlib per creare grafici dinamici:
# Python con matplotlib esempio
import mysql.connector
import matplotlib.pyplot as plt
# Connessione al database
conn = mysql.connector.connect(
host="localhost",
user="root",
password="password",
database="mio_database"
)
cursor = conn.cursor()
cursor.execute("SELECT categoria, SUM(quantita) FROM prodotti GROUP BY categoria")
# Preparare dati per il grafico
categorie = []
quantita = []
for row in cursor:
categorie.append(row[0])
quantita.append(row[1])
# Creare grafico a barre
plt.bar(categorie, quantita)
plt.xlabel('Categoria')
plt.ylabel('Quantità')
plt.title('Vendite per Categoria')
plt.show()
Metodo 3: Strumenti di Business Intelligence
Collega MySQL a strumenti professionali come:
- Power BI (Microsoft) - Grafici interattivi e dashboard
- Tableau - Visualizzazioni avanzate
- Google Data Studio - Reporting gratuito
- Metabase - Open source per dashboard SQL
Query Utili per Dashboard Aziendali
-- KPI: Tasso di crescita mensile
SELECT
DATE_FORMAT(data_ordine, '%Y-%m') AS mese,
COUNT(*) AS ordini,
SUM(totale) AS fatturato,
AVG(totale) AS ticket_medio
FROM ordini
WHERE data_ordine >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH)
GROUP BY mese
ORDER BY mese;
-- Top 10 prodotti più venduti (per grafico a barre orizzontale)
SELECT
p.nome_prodotto,
SUM(o.quantita) AS unita_vendute,
SUM(o.quantita * p.prezzo) AS ricavo_totale
FROM ordini_dettagli o
JOIN prodotti p ON o.prodotto_id = p.id
GROUP BY p.nome_prodotto
ORDER BY unita_vendute DESC
LIMIT 10;
-- Distribuzione geografica clienti (per mappa heat map)
SELECT
regione,
citta,
COUNT(*) AS numero_clienti,
SUM(totale_acquisti) AS valore_clienti
FROM clienti
GROUP BY regione, citta
ORDER BY numero_clienti DESC;
Best Practices per Query di Visualizzazione
- Usa alias chiari (AS) per rendere le colonne leggibili nei grafici
- Limita i risultati con TOP/LIMIT per evitare grafici sovraffollati
- Arrotonda i decimali con ROUND() per una visualizzazione più pulita
- Ordina i dati in modo logico (crescente/decrescente) prima dell'export
- Usa DATE_FORMAT() per formattare le date in modo user-friendly
- Calcola percentuali e medie direttamente in SQL quando possibile
Esempio Completo: Dashboard Vendite
Ecco un set di query per creare una dashboard completa di analisi vendite:
-- 1. Panoramica KPI principali
SELECT
COUNT(DISTINCT id) AS totale_ordini,
COUNT(DISTINCT cliente_id) AS clienti_attivi,
SUM(totale) AS fatturato_totale,
AVG(totale) AS valore_medio_ordine
FROM ordini
WHERE YEAR(data_ordine) = YEAR(CURDATE());
-- 2. Andamento vendite settimanali (grafico a linea)
SELECT
WEEK(data_ordine) AS settimana,
COUNT(*) AS ordini,
SUM(totale) AS fatturato
FROM ordini
WHERE data_ordine >= DATE_SUB(CURDATE(), INTERVAL 12 WEEK)
GROUP BY settimana
ORDER BY settimana;
-- 3. Categoria prodotti più venduta (grafico a torta)
SELECT
categoria,
COUNT(*) AS prodotti_venduti,
ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM ordini_dettagli), 1) AS percentuale
FROM ordini_dettagli od
JOIN prodotti p ON od.prodotto_id = p.id
GROUP BY categoria
ORDER BY prodotti_venduti DESC;
-- 4. Confronto performance anno su anno
SELECT
YEAR(data_ordine) AS anno,
MONTH(data_ordine) AS mese,
SUM(totale) AS fatturato
FROM ordini
WHERE YEAR(data_ordine) IN (YEAR(CURDATE()), YEAR(CURDATE()) - 1)
GROUP BY anno, mese
ORDER BY anno, mese;
Attenzione alle Performance
Query complesse con JOIN multipli e funzioni di aggregazione possono essere lente su tabelle grandi. Considera di creare VIEW o tabelle aggregate per dashboard che vengono consultate frequentemente. Usa anche EXPLAIN per analizzare le query lente.
Esercizi Pratici
Esercizio 1: Query SELECT Avanzate
Scenario: Hai una tabella "prodotti" con colonne: id, nome, categoria, prezzo, quantita.
Richieste:
- Seleziona tutti i prodotti con prezzo superiore a 50€
- Trova i 5 prodotti più costosi
- Calcola il prezzo medio per ogni categoria
- Trova le categorie con più di 10 prodotti
- Seleziona i prodotti con quantità inferiore a 5 (quasi esauriti)
Esercizio 2: JOIN tra Tabelle
Scenario: Hai due tabelle: "clienti" (id, nome, email) e "ordini" (id, cliente_id, data, totale).
Richieste:
- Mostra tutti gli ordini con nome e email del cliente
- Trova i clienti che non hanno mai fatto ordini
- Calcola il totale speso da ogni cliente
- Trova i 3 clienti che hanno speso di più
Esercizio 3: Inserimento e Modifica Dati
Scenario: Tabella "dipendenti" con colonne: id, nome, cognome, reparto, stipendio.
Richieste:
- Inserisci 3 nuovi dipendenti
- Aumenta lo stipendio del 10% per tutti i dipendenti del reparto "Vendite"
- Cambia il reparto di un dipendente specifico
- Elimina tutti i dipendenti con stipendio inferiore a 20.000€
- Crea un indice sulla colonna "reparto"
Esercizio 4: Query Complesse
Scenario: Database di una scuola con tabelle: "studenti", "corsi", "iscrizioni" (studente_id, corso_id, voto).
Richieste:
- Trova tutti gli studenti iscritti al corso "Informatica"
- Calcola la media dei voti per ogni studente
- Trova gli studenti con media superiore a 7
- Mostra i corsi con il maggior numero di iscritti
- Trova gli studenti che non sono iscritti a nessun corso
Esercizio 5: Preparare Dati per Grafici
Scenario: Database e-commerce con tabelle: "ordini" (id, data, totale, stato), "prodotti" (id, nome, categoria, prezzo).
Richieste:
- Scrivi una query per un grafico a barre che mostri il numero di ordini per mese negli ultimi 12 mesi
- Crea una query per un grafico a torta che mostri la distribuzione percentuale degli ordini per stato (In Attesa, Spedito, Consegnato, Annullato)
- Prepara i dati per un grafico a linea che mostri il fatturato giornaliero dell'ultimo mese
- Crea una query per un grafico a barre orizzontale con le top 5 categorie prodotti più redditizie
- Scrivi una query che calcoli il tasso di crescita percentuale del fatturato mese su mese
Suggerimento: Usa DATE_FORMAT(), GROUP BY, aggregazioni (SUM, COUNT, AVG) e ordina i risultati in modo appropriato per il tipo di grafico.
Suggerimento finale
Prima di scrivere query complesse, prova a suddividerle in passaggi più semplici. Usa SELECT per verificare i dati prima di eseguire UPDATE o DELETE. Testa sempre le tue query su un database di sviluppo prima di applicarle in produzione!
Hai completato tutte le 4 lezioni del corso di Introduzione alla Programmazione. Ora è il momento di mettere alla prova le tue conoscenze con il test finale: domande su algoritmi, programmazione, MySQL e digital soft skills, con risultati immediati e report scaricabile.
Vuoi approfondire la programmazione con un percorso completo?
Scopri i corsi e la formazione di Federico Boggia su programmazione, dati e digitale.