In informatica, un cursore dei database è una struttura che permette di scorrere i record restituiti da una query. Essi possono essere di sola lettura o, se l'implementazione lo consente, possono essere usati per modificare o cancellare le righe. I cursori sono più veloci se sono in grado di muoversi soltanto in avanti, ma se l'implementazione lo consente è possibile creare cursori in grado di spostarsi in entrambe le direzioni. Possono essere usati all'interno di una stored procedure, o di un programma esterno, per elaborare l'insieme dei risultati una riga per volta. In particolare, solo grazie ai cursori le stored procedure possono implementare una business logic complessa che legge più tuple da una tabella e le esamina una per una.
Prima di poter utilizzare un cursore in qualsiasi modo, è necessario dichiararlo. La sintassi standard SQL per dichiarare un cursore è la seguente:
DECLARE nome_cursore CURSOR FOR query;
La query non può essere una stringa, di conseguenza gli standard non prevedono la possibilità di abbinare i cursori a comandi SQL dinamici.
Per dichiarare un cursore in grado di spostarsi indietro, è necessario specificare l'opzione SCROLL
:
DECLARE nome_cursore SCROLL CURSOR FOR query;
Dopo aver dichiarato un cursore, è possibile aprirlo:
OPEN nome_cursore;
È a questo punto che, di solito, le implementazioni di SQL eseguono la query associata al cursore. Ogni operazione di lettura e di modifica può essere eseguita solo dopo l'apertura (e prima della chiusura) del cursore.
È possibile spostare il cursore alla riga successiva (o alla prima riga, se nessuna riga è stata letta) tramite il seguente comando:
FETCH nome_cursore INTO lista_variabili;
Le variabili devono essere tante quante i valori estratti dalla SELECT
per ogni riga. I nomi delle variabili devono essere separate da virgole e, opzionalmente, da spaziature di qualsiasi tipo.
Se il tipo di cursore e il DBMS lo permettono, è anche possibile spostarsi indietro, alla prima riga, all'ultima o in una posizione arbitraria.
Per spostarsi alla riga successiva è possibile specificare esplicitamente l'opzione NEXT
, che comunque è il default. Per spostarsi alla riga precedente si usa PRIOR
, per la prima FIRST
e per l'ultima LAST
. Esempi:
FETCH NEXT FROM cursor_name;
FETCH PREV FROM cursor_name;
È anche possibile specificare un valore numerico, che rappresenta il numero progressivo della riga che si desidera selezionare. Questo valore può essere assoluto o relativo. Se è relativo, un numero positivo indica uno spostamento in avanti, mentre un numero negativo indica uno spostamento indietro:
FETCH ABSOLUTE n FROM cursor_name;
FETCH RELATIVE n FROM cursor_name;
Lo standard SQL:2003 prevede la possibilità di eseguire un'istruzione DELETE
o UPDATE
sulla riga selezionata da un dato cursore. Per fare questo si utilizza la clausola CURRENT OF
all'interno della clausola WHERE
:
DELETE
FROM nome_tabella
WHERE CURRENT OF nome_cursore;
UPDATE nome_tabella
SET modifiche
WHERE CURRENT OF nome_cursore;
Per liberare la memoria occupata dai risultati della query e dal cursore stesso è necessario chiudere il cursore:
CLOSE nome_cursore;
Vi sono casi in cui i cursori sono lungi dall'essere la soluzione più efficiente e programmatori che non amano utilizzarli. Spesso esistono delle alternative all'uso dei cursori.
Alcuni DBMS permettono di utilizzare la sintassi SELECT ... INTO
per inserire alcuni valori in altrettante variabili. Questo è certamente il modo più efficiente di leggere uno o più valori da una sola tupla. In questi casi, è buona pratica evitare di utilizzare un cursore.
Le JOIN SQL possono essere piuttosto complicate da scrivere, soprattutto se non si conosce bene la teoria relazionale. A volte i cursori vengono utilizzati proprio per evitare di scrivere JOIN
complesse: si esegue un ciclo su una tabella utilizzando un cursore, e per ogni record si compone dinamicamente una query che leggerà un'altra tabella. Tuttavia una JOIN
è sempre più efficiente di un cursore.
MySQL e MariaDB supportano il comando HANDLER
, che permette di scorrere le righe di una tabella in modo simile ai cursori. Non si basa però su una query. È possibile specificare una condizione, mentre è necessario che i record vengano ordinati in base a un indice. HANDLER
può essere visto come un cursore di sola lettura piuttosto limitato.