CodeGym /Corsi /SQL SELF /Lavorare con le transazioni per garantire l'integrità dei...

Lavorare con le transazioni per garantire l'integrità dei dati

SQL SELF
Livello 22 , Lezione 2
Disponibile

Immagina di scrivere un'app per un e-commerce e, durante il pagamento di un ordine, devi:

  1. Bloccare i soldi dalla carta del cliente.
  2. Diminuire la quantità del prodotto in magazzino.
  3. Creare una voce sulla transazione avvenuta con successo.

Cosa succede se qualcosa va storto a metà di queste azioni? Tipo, il prodotto finisce in magazzino dopo che i soldi sono stati bloccati, ma prima che venga creata la voce dell’ordine? È un casino: i soldi sono "sospesi", l’ordine non è completato e il tuo server riceve una valanga di mail arrabbiate (e magari anche qualche causa legale).

Le transazioni servono proprio a evitare queste situazioni. Ti permettono di raggruppare più operazioni in un’unica unità "atomica" di lavoro con il database. È come il tasto "Annulla" in un editor di testo: se qualcosa va storto, puoi semplicemente tornare all’inizio.

Come le transazioni garantiscono l'integrità dei dati?

Le transazioni si basano sul concetto di ACID:

  • Atomicità (Atomicity) — Tutte le operazioni dentro una transazione vengono eseguite tutte insieme o nessuna. "Tutto o niente".
  • Consistenza (Consistency) — I dati restano in uno stato consistente prima e dopo la transazione.
  • Isolamento (Isolation) — Una transazione non disturba le altre.
  • Durabilità (Durability) — Quando una transazione è completata, il suo risultato resta anche se il sistema va giù.

Perché lo ripeto ancora? Perché è l’ideale a cui tutti puntano. E... che raramente si raggiunge davvero. Quando torneremo sulle transazioni più avanti nel corso, capirai che dovremo sacrificare alcuni principi ACID.

Quindi goditi il momento in cui le transazioni sembrano semplici e perfette. E passiamo subito agli esempi!

Esempio di utilizzo delle transazioni

Vediamo uno scenario in cui aggiungiamo uno studente e lo iscriviamo a un corso.

Supponiamo di lavorare con il database di un’università. Abbiamo degli ascoltatori esterni ai nostri corsi. Se c’è posto nel corso, registriamo questo ascoltatore come studente (temporaneamente) e lo aggiungiamo al corso. Ecco come funziona.

Quando aggiungiamo un nuovo studente al database e lo iscriviamo a un corso dobbiamo:

  1. Aggiungere una voce nella tabella students.
  2. Creare una voce nella tabella enrollments che collega lo studente al corso.

Se qualcosa va storto (tipo il corso è già pieno), dobbiamo annullare l’operazione per non avere dati incoerenti tra le tabelle. Ecco come si fa:

-- Inizio transazione
BEGIN;

-- Passo 1: Aggiungiamo lo studente
INSERT INTO students (name, age, gender)
VALUES ('Otto Lin', 20, 'Maschio')
RETURNING id;

-- Supponiamo che sia tornato id = 10

-- Passo 2: Lo iscriviamo al corso
INSERT INTO enrollments (student_id, course_id)
VALUES (10, 5);

-- Tutto ok? Salviamo le modifiche
COMMIT;

Cosa succede se c'è un errore?

Metti che c’è un errore durante l’iscrizione al corso: tipo il corso non esiste. Se ti dimentichi della transazione, la voce dello studente resta nella tabella students, ma in enrollments non c’è nulla. Questo rompe l’integrità dei dati. Per evitarlo, possiamo usare il comando ROLLBACK.

-- Inizio transazione
BEGIN;

-- Passo 1: Aggiungiamo lo studente
INSERT INTO students (name, age, gender)
VALUES ('Otto Lin', 20, 'Maschio')
RETURNING id;

-- Passo 2: Proviamo a iscriverlo al corso
INSERT INTO enrollments (student_id, course_id)
VALUES (10, 999); -- Errore: il corso con id = 999 non esiste!

-- Annulliamo tutte le modifiche
ROLLBACK;

Così nessuna delle operazioni viene eseguita e il database resta come prima della transazione.

Uso di SAVEPOINT per il controllo

Ora immaginiamo uno scenario più complesso. Vuoi fare più operazioni, ma a un certo punto vuoi tornare solo a un punto preciso, non annullare tutto il processo.

Facciamo una registrazione passo-passo dello studente

-- Inizio transazione
BEGIN;

-- Aggiungiamo lo studente
SAVEPOINT add_student; -- Creiamo un punto di salvataggio
INSERT INTO students (name, age, gender)
VALUES ('Anna Song', 22, 'Femmina');

-- La iscriviamo al primo corso
SAVEPOINT enroll_course_1; -- Un altro punto di salvataggio
INSERT INTO enrollments (student_id, course_id)
VALUES (11, 5);

-- La iscriviamo al secondo corso (qui c'è un errore)
INSERT INTO enrollments (student_id, course_id)
VALUES (11, 999); -- Errore!

-- Torniamo solo all'ultimo punto di salvataggio
ROLLBACK TO enroll_course_1;

-- Riprendiamo il processo
INSERT INTO enrollments (student_id, course_id)
VALUES (11, 6);

-- Salviamo le modifiche
COMMIT;

Così, gli errori in una parte del processo non bloccano il salvataggio dei dati nelle altre.

Verifica delle modifiche

Se una query SQL cambia qualcosa, puoi controllare se davvero ci sono state modifiche oppure no.

Può capitare che fai un DELETE, ma nessuna riga corrisponde al WHERE. Oppure fai un UPDATE, ma i dati erano già aggiornati e in pratica non cambia nulla.

Per questo c’è una variabile di sistema speciale: FOUND. Indica se sono state toccate delle righe nell’ultima query SQL:

  • FOUND = TRUE — la query ha aggiornato/cancellato qualcosa;
  • FOUND = FALSE — non è stato cancellato o cambiato nulla.

Con un normale SELECT non funziona, serve solo per tracciare le modifiche.

Applicazione pratica: gestione dei pagamenti

Le transazioni sono super utili nelle app finanziarie. Riprendiamo un sistema che deve trasferire soldi da un conto all’altro.

-- Inizio transazione
BEGIN;

-- Passo 1: Togliamo i soldi dal primo conto
UPDATE accounts
SET balance = balance - 100
WHERE id = 1 AND balance >= 100;

-- Passo 2: Controlliamo che l'operazione sia andata a buon fine (righe modificate)
IF NOT FOUND THEN
    ROLLBACK; -- Annulla se i fondi non bastano
    RAISE EXCEPTION 'Fondi insufficienti!'; -- Errore! Lanciamo un'eccezione
END IF;

-- Passo 3: Aggiungiamo i soldi al secondo conto
UPDATE accounts
SET balance = balance + 100
WHERE id = 2;

-- Salviamo la transazione
COMMIT;

Qui, se il cliente prova a trasferire più soldi di quanti ne ha sul conto, la transazione viene annullata e il database non resta in uno stato "sospeso".

Particolarità ed errori tipici

Dimenticato COMMIT: se alla fine della transazione ti dimentichi di fare COMMIT, il database resta "in attesa" e le modifiche non vengono salvate.

Dimenticato WHERE: aggiornare o cancellare dati senza condizione può essere un disastro. Tipo, DELETE FROM students senza WHERE cancella tutti gli studenti.

Transazioni troppo lunghe: se una transazione resta aperta troppo a lungo, può bloccare l’accesso ai dati e creare problemi di performance. Chiudi sempre le transazioni (COMMIT o ROLLBACK) il più velocemente possibile.

Le transazioni sono il tuo unico vero amico quando si tratta di garantire l’integrità dei dati. Aiutano a evitare inconsistenze, soprattutto in scenari complessi come la registrazione utenti, la gestione dei pagamenti o l’aggiornamento di tabelle collegate. Se impari a usare BEGIN, COMMIT, ROLLBACK e SAVEPOINT, potrai creare applicazioni molto più affidabili e sicure.

2
Compito
SQL SELF, livello 22, lezione 2
Bloccato
Basi sull'utilizzo delle transazioni
Basi sull'utilizzo delle transazioni
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION