ProgrammazioneSviluppatore SQL

Descrivi le caratteristiche, i vantaggi e le insidie della gestione sequenziale dei dati tramite cursori (CURSOR) in SQL. Quando è opportuno utilizzare i cursori e quando è meglio evitarli a favore delle operazioni basate su insiemi (set-based)? Fai un esempio di utilizzo di un cursore.

Supera i colloqui con l'assistente IA Hintsage

Risposta.

Cursori in SQL permettono di iterare attraverso un insieme di dati riga per riga e di eseguire determinate azioni su ciascun record, similmente all'iterazione nella programmazione. Questo è utile per logiche passo-passo complesse che non possono essere realizzate con una singola query SQL (ad esempio, calcoli passo-passo con stato esterno modificabile).

Esempio di cursore:

DECLARE my_cursor CURSOR FOR SELECT id, balance FROM Accounts WHERE isActive = 1; OPEN my_cursor; DECLARE @id INT, @bal DECIMAL(10,2); FETCH NEXT FROM my_cursor INTO @id, @bal; WHILE @@FETCH_STATUS = 0 BEGIN UPDATE Accounts SET balance = @bal * 1.05 WHERE id = @id; FETCH NEXT FROM my_cursor INTO @id, @bal; END CLOSE my_cursor; DEALLOCATE my_cursor;

Vantaggi:

  • Permettono di implementare procedure complesse dove ogni riga necessita di un trattamento o di una verifica di condizioni esterne.
  • Comodi per integrazioni con sistemi legacy o migrazioni dove è necessario un controllo passo-passo.

Svantaggi e insidie:

  • I cursori sono generalmente MOLTO lenti: il trattamento avviene riga per riga, anziché in modo basato su insiemi;
  • Richiedono molte risorse e possono bloccare le tabelle;
  • Non scalano bene per grandi volumi di dati;
  • È meglio riscrivere il codice in SQL basato su insiemi o utilizzare l'elaborazione batch.

Domanda insidiosa.

È possibile utilizzare un cursore all'interno di un trigger? Quali conseguenze può avere per le prestazioni del database?

Risposta ed esempio: L'uso di cursori all'interno di un trigger è possibile, ma estremamente sconsigliato: ogni operatore DML può attivare un cursore per ogni riga interessata, il che porta a un aumento esponenziale delle query e dei blocchi.

CREATE TRIGGER UpdateBalance ON Accounts AFTER INSERT AS DECLARE c CURSOR FOR SELECT id FROM inserted; -- male! OPEN c; -- ...

Storia

Progetto: Riepilogo ordini nel retail. Vi era la necessità di calcolare le scorte a magazzino se nella partita veniva trovata una merce difettosa: le partite successive necessitavano di un conteggio manuale tramite aggiornamento degli sconti. È stato utilizzato un cursore per iterare attraverso le partite. In seguito si è scoperto che, dopo ripetuti avvii, il flusso veniva bloccato per ore, il carico sul server aumentava in modo esponenziale e la contesa dei lock causava fallimenti.


Storia

Progetto: ERP, migrazione dati. Nel processo di gestione dell'importo sono stati aggiunti dei controlli degli errori tramite cursore. Non si è tenuto conto che su 5 milioni di righe il cursore funziona 40 volte più lentamente rispetto a un'elaborazione batch tramite SET-BASED UPDATE + CASE. A causa della migrazione lenta, le scadenze sono slittate.


Storia

Progetto: Billing di una compagnia finanziaria. In un trigger per l'aggiornamento della tabella dei movimenti è stato aggiunto un cursore per calcolare il nuovo saldo aggregato. In produzione ciò ha portato a una situazione di "STOP THE WORLD": l'inserimento di anche solo un record ha rallentato il servizio a causa dell'attivazione di un cursore annidato su molte righe.