Home/ Corsi/ Programmazione/ Lezione 4: Query Avanzate e Modifica Dati

Lezione 4: Query Avanzate e Modifica Dati

UF2: impara a manipolare i dati con INSERT, UPDATE, DELETE, REPLACE, TRUNCATE e LOAD DATA, a interrogare database complessi con query avanzate e JOIN, e a preparare i dati per i grafici ottimizzando le performance.

Query avanzate e modifica dati con MySQL
Indice dei contenuti
UF 2 - MySQL 6 ore

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:

  1. Seleziona tutti i prodotti con prezzo superiore a 50€
  2. Trova i 5 prodotti più costosi
  3. Calcola il prezzo medio per ogni categoria
  4. Trova le categorie con più di 10 prodotti
  5. 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:

  1. Mostra tutti gli ordini con nome e email del cliente
  2. Trova i clienti che non hanno mai fatto ordini
  3. Calcola il totale speso da ogni cliente
  4. 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:

  1. Inserisci 3 nuovi dipendenti
  2. Aumenta lo stipendio del 10% per tutti i dipendenti del reparto "Vendite"
  3. Cambia il reparto di un dipendente specifico
  4. Elimina tutti i dipendenti con stipendio inferiore a 20.000€
  5. 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:

  1. Trova tutti gli studenti iscritti al corso "Informatica"
  2. Calcola la media dei voti per ogni studente
  3. Trova gli studenti con media superiore a 7
  4. Mostra i corsi con il maggior numero di iscritti
  5. 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:

  1. Scrivi una query per un grafico a barre che mostri il numero di ordini per mese negli ultimi 12 mesi
  2. Crea una query per un grafico a torta che mostri la distribuzione percentuale degli ordini per stato (In Attesa, Spedito, Consegnato, Annullato)
  3. Prepara i dati per un grafico a linea che mostri il fatturato giornaliero dell'ultimo mese
  4. Crea una query per un grafico a barre orizzontale con le top 5 categorie prodotti più redditizie
  5. 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.

Vai ai corsi