CodeGym /Corsi /SQL SELF /Analisi degli errori tipici durante il debug e l’ottimizz...

Analisi degli errori tipici durante il debug e l’ottimizzazione di PL/pgSQL

SQL SELF
Livello 56, Lezione 4
Disponibile

Oggi, per concludere questo epico viaggio nel mondo di PL/pgSQL, analizziamo gli errori più comuni che possono aspettarti durante il debug e l’ottimizzazione di funzioni e procedure. Conoscere questi errori ti aiuterà non solo a evitarli in futuro, ma anche a risolvere i bug in modo più efficace se dovessero comparire.

Errori tipici durante il debug e l’ottimizzazione

1. Uso scorretto delle variabili

Uno degli errori più frequenti quando si scrivono e si fanno debug di funzioni in PL/pgSQL è dichiarare o usare male le variabili. Per esempio, se ti dimentichi di specificare esplicitamente il tipo di una variabile o ti confondi con i valori passati tramite i parametri. Vediamo come può succedere nella pratica:

CREATE OR REPLACE FUNCTION calculate_discount(order_total NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
    discount_rate NUMERIC;
BEGIN
    -- Ops! Ho dimenticato di inizializzare la variabile discount_rate
    RETURN order_total * discount_rate;
END;
$$ LANGUAGE plpgsql;

Quando chiami questa funzione otterrai un errore legato all’uso di NULL nei calcoli, perché la variabile discount_rate non è inizializzata.

Come evitarlo:

  1. Assegna sempre un valore di default alle variabili quando le dichiari:
   DECLARE
       discount_rate NUMERIC := 0.1; -- Valore di default
  1. Controlla l’uso delle variabili con RAISE NOTICE per assicurarti che abbiano i valori che ti aspetti:
RAISE NOTICE 'Valore di discount_rate: %', discount_rate;

2. Mancanza di logging degli errori

Un altro problema comune è la mancanza di un meccanismo di logging. Se qualcosa va storto e non logghi cosa succede nella tua funzione, è come cercare un gatto nero in una stanza buia, soprattutto se non sei nemmeno sicuro che il gatto ci sia davvero.

Ecco un esempio di funzione senza logging:

CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS VOID AS $$
BEGIN
    -- Logica complicata di gestione dell’ordine
    UPDATE orders SET status = 'processed' WHERE id = order_id;
END;
$$ LANGUAGE plpgsql;

E se order_id viene passato in modo sbagliato? E se la riga nella tabella orders non esiste?

Come evitarlo: Aggiungi RAISE NOTICE o RAISE EXCEPTION per loggare i passaggi critici:

CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS VOID AS $$
BEGIN
    -- Logghiamo i dati in ingresso
    RAISE NOTICE 'Gestione dell’ordine con ID %', order_id;

    -- Logica complicata di gestione
    UPDATE orders SET status = 'processed' WHERE id = order_id;

    -- Logghiamo il risultato
    RAISE NOTICE 'Stato dell’ordine aggiornato per ID %', order_id;
END;
$$ LANGUAGE plpgsql;

Così puoi facilmente capire dove si verifica l’errore grazie ai messaggi stampati.

3. Ignorare la performance delle query

Questo è uno dei principali nemici di chi lavora con i database. Per esempio, scrivi una funzione che sembra ok, ma va lentissima. Una delle cause principali delle query lente è la mancanza di indici o piani di esecuzione inefficienti.

Esempio di query lenta:

CREATE OR REPLACE FUNCTION get_large_orders()
RETURNS TABLE(order_id INT, total NUMERIC) AS $$
BEGIN
    RETURN QUERY
    SELECT id, total FROM orders WHERE total > 1000;
END;
$$ LANGUAGE plpgsql;

Se il campo total nella tabella orders non è indicizzato, la query farà una scansione completa della tabella, che è super inefficiente.

Come evitarlo:

  1. Usa EXPLAIN ANALYZE per controllare l’efficienza delle query:
EXPLAIN ANALYZE SELECT id, total FROM orders WHERE total > 1000;
  1. Crea indici sulle colonne usate spesso:
CREATE INDEX idx_orders_total ON orders(total);

4. Uso del livello di isolamento delle transazioni sbagliato

Quando esegui procedure complesse, a volte capitano errori perché non si capisce bene come funzionano i livelli di isolamento delle transazioni. Per esempio, se due transazioni cercano di aggiornare la stessa riga contemporaneamente, può succedere un deadlock.

Esempio di potenziale deadlock:

BEGIN;
UPDATE orders SET status = 'processed' WHERE id = 1;

-- Aspettando il lock di un’altra transazione
UPDATE inventory SET stock = stock - 1 WHERE product_id = 100;
COMMIT;

Se un’altra transazione prova a fare queste operazioni in ordine diverso, ottieni un blocco reciproco.

Come evitarlo:

  1. Pensa bene all’ordine delle operazioni e seguilo sempre.
  2. Usa il livello di isolamento SERIALIZABLE se serve.

5. Mancanza di gestione degli errori

Gestire gli errori non è solo una buona pratica, ma anche uno strumento per rendere il tuo codice più stabile. Per esempio, nel codice qui sotto manca la gestione degli errori possibili:

CREATE OR REPLACE FUNCTION add_order(order_id INT)
RETURNS VOID AS $$
BEGIN
    INSERT INTO orders (id, status) VALUES (order_id, 'new');
END;
$$ LANGUAGE plpgsql;

Se per caso order_id esiste già, otterrai l’errore duplicate key value violates unique constraint.

Come evitarlo: Usa i blocchi di gestione delle eccezioni:

CREATE OR REPLACE FUNCTION add_order(order_id INT)
RETURNS VOID AS $$
BEGIN
    INSERT INTO orders (id, status) VALUES (order_id, 'new');
EXCEPTION WHEN unique_violation THEN
    RAISE NOTICE 'Ordine con ID % esiste già!', order_id;
END;
$$ LANGUAGE plpgsql;

Esempi di errori e come correggerli

Errore 1: Le query sono lente perché manca l’indicizzazione

Situazione: Hai una query che filtra una tabella per una colonna, ma quella colonna non ha un indice.

Correzione: Crea un indice sulla colonna giusta.

Errore 2: La logica della funzione è confusa e difficile da debuggare

Situazione: La funzione contiene troppa logica e non è suddivisa in sotto-funzioni.

Correzione: Dividi la funzione complessa in funzioni più piccole. Così il codice è più leggibile e il debug è più semplice.

Errore 3: Uso scorretto di RAISE EXCEPTION

Situazione: RAISE EXCEPTION viene usato per tutti gli errori, anche quelli poco importanti.

Correzione: Usa RAISE NOTICE per i messaggi informativi e solo RAISE EXCEPTION per i casi critici.

RAISE NOTICE 'Tutto sotto controllo — questa fase della funzione è finita.';
RAISE EXCEPTION 'Qualcosa si è rotto! Dai un’occhiata ai parametri di input.';

Consigli per evitare errori

  1. Aggiungi logging: nei punti critici della tua funzione usa RAISE NOTICE per tracciare cosa succede.
  2. Testa le funzioni: usa regolarmente dati di test per controllare funzioni e procedure.
  3. Mantieni il codice leggibile: dividi le funzioni complesse in sotto-funzioni e procedure più piccole.
  4. Analizza la performance: usa EXPLAIN ANALYZE per assicurarti che le tue query vadano veloci.
  5. Preparati agli imprevisti: aggiungi sempre blocchi di gestione delle eccezioni per gestire gli errori.
EXCEPTION
    WHEN OTHERS THEN
        RAISE EXCEPTION 'Si è verificato un errore imprevisto: %', SQLERRM;

Così potrai gestire gli errori con sicurezza e prevenirli in futuro.

2
Compito
SQL SELF, livello 56, lezione 4
Bloccato
Gestione degli errori in caso di valore duplicato
Gestione degli errori in caso di valore duplicato
1
Sondaggio/quiz
Ottimizzazione delle funzioni, livello 56, lezione 4
Non disponibile
Ottimizzazione delle funzioni
Ottimizzazione delle funzioni
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION