Tecnicamente per ciclo o loop si intende un costrutto che determina la ripetizione di una o più istruzioni fino a quando una determinata condizione risulta soddisfatta o vera. I cicli sono molto utilizzati sia nei linguaggi di programmazione che in quelli di scripting e possono essere sfruttati anche per la definizione di Stored Procedures.
Per la generazione di loop nelle Stored Procedures abbiamo a disposizione tre standard:
- cicli basati su WHILE;
- cicli basati su REPEAT;
- cicli basati su LOOP;
Cicli WHILE ..AND WHILE
Un ciclo WHILE consente la ripetizione di istruzioni fino a quando una determinata condizione questa risulta vera. La sintassi utilizzata per un ciclo WHILE è molto semplice:
WHILE condizione DO statements END WHILE
Dove condizione
determina l'iterazione del ciclo fino a quando rimane vera, mentre statements
rappresenta un numero variabile di istruzioni che devono essere eseguite ad ogni iterazione; le iterazioni spesso agiscono direttamente sul valore associato alla condizione tanto da modificarlo influendo sugli esiti stessi del ciclo.
Vediamo un semplice esempio:
CREATE PROCEDURE proc () BEGIN DECLARE var INT; SET var = 0; WHILE var < 10 DO INSERT INTO tbl VALUES (var); SET var = var + 1; END WHILE; END; //
Scomponiamo il codice appena proposto:
- innanzitutto abbiamo definito il nome della procedura (
proc()
); - abbiamo poi aperto il blocco di istruzioni della stored procedure tramite il delimitatore
BEGIN
; - è stata quindi introdotta una variabile (
var
) alla quale è stato associato un tipo di dato numerico intero (INT
); - il valore iniziale della variabile è stato settato come pari a zero;
- è stato quindi inizializzato un ciclo
WHILE
, il loop verrà iterato fino a quando risulterà vera la condizione introdotta nel costrutto, cioè fino a quando il valore della variabile sarà inferiore a 10; - con il comando
DO
è stata introdotta l'istruzione che dovrà essere iterata (cioè il comportamento che dovrà essere ripetuto), nel caso specifico abbiamo un inserimento in tabella di un valore pari a quello acquisito dalla variabile ad ogni iterazione, inoltre, abbiamo un incremento unitario della variabile che verrà ripetuto fino a quando il suo valore sarà inferiore a 10; END WHILE
è stato utilizzato per chiudere il blocco di istruzioni relative al ciclo;END; //
ha chiuso il blocco relativo alla definizione della stored procedure e lo ha isolato dal resto delle istruzioni SQL. Ricordiamo infatti che mentre il bloccoBEGIN...END
isola un gruppo di istruzioni all'interno di una stored procedure, il carattere di delimitazione definito dall'utente (nel nostro caso//
) isola l'intera definizione della procedura dal resto dei comandi SQL.
La stored procedure che abbiamo definito non prevede il passaggio di parametri in chiamata, quindi per poter essere utilizzata basterà un riferimento al nome:
mysql> CALL proc()// Query OK, 1 row affected (0.00 sec)
La chiamata appena descritta potrà essere utilizzata per tutte le altre procedure descritte nel corso di questa trattazione.
Diversamente da quello che si potrebbe aspettare, l'output prodotto da MySQL riporterà il risultato dell'interrogazione a carico di una sola riga ("1 row affected"); non si tratta di un errore, infatti, nella restituzione della risposta, il server prenderà in considerazione soltanto l'ultimo inserimento effettuato.
Cicli con REPEAT ..END REPEAT
Un ciclo basato su REPEAT
funziona praticamente nello stesso modo di un loop basato su WHILE
, vi è però una fondamentale differenza: mentre con WHILE
la condizione viene definita (e quindi presa in considerazione) prima dell'istruzione da iterare, con REPEAT
succede esattamente il contrario.
Quindi in questo caso per i cicli basati su REPEAT
dovremo utilizzare una sintassi simile alla seguente:
REPEAT statements UNTIL condizione END REPEAT
Quindi, "traducendo" letteralmente dal linguaggio SQL abbiamo qualcosa come "ripeti le istruzioni fino a quando (UNTIL
) la condizione è vera, dopodiché interrompi l'iterazione (END REPEAT
)".
Vediamo di seguito un semplice esempio:
CREATE PROCEDURE proc () BEGIN DECLARE var INT; SET var = 0; REPEAT INSERT INTO tbl VALUES (var); SET var = var + 1; UNTIL var >= 10 END REPEAT; END; //
Anche in questo caso si è proceduto inizialmente alla definizione del nome della stored procedures, si nota subito come per essa non sia stato previsto alcun parametro; segue poi il delimitatore BEGIN che racchiude l'unico blocco di istruzioni della procedura e l'introduzione di una variabile (inizialmente pari a zero) che assumerà diversi valori sulla base delle iterazioni del ciclo.
REPEAT
è la clausola che inizializza il blocco di istruzioni relativo al ciclo vero e proprio, ad essa seguono le istruzioni che dovranno essere eseguite durante le iterazioni previste dal ciclo sulla base della condizione introdotta successivamente. Nel caso specifico le istruzioni non sono altro che un semplice statement per l'inserimento e un incremento unitario della variabile.
UNTIL
è invece la chiave che introduce la nostra condizione, essa stabilisce infatti che le istruzioni previste dal ciclo dovranno essere ripetute fino a quando il valore della variabile sarà maggiore o uguale a 10. Da notare come dopo l'istruzione UNTIL e la condizione introdotta non sia stato utilizzato il punto e virgola.
REPEAT
è un costrutto non nuovo a chi utilizza MySQL, si tratta infatti anche del nome di una funzione che permette di ripetere delle stringhe un numero predefinito di volte, creando appunto dei semplici cicli.
Se infatti analizziamo per esempio l'istruzione seguente:
SELECT REPEAT("stored procedure", 9);
Questa ci permetterà di stampare 9 volte di seguito la stringa "stored procedure".
Ciclo con LOOP ..END LOOP
Il ciclo basato su LOOP si differenzia sia dal ciclo WHILE sia da quello REPEAT per via del fatto che non presenta una condizione né all'inizio né alla fine. La sintassi fondamentale di un ciclo basato su LOOP è semplicissima:
LOOP statements END LOOP
Per il controllo del flusso di un ciclo LOOP sono disponibili due istruzioni: LEAVE
e ITERATE
; mentre la prima svolge la funzione di abbandonare il flusso di iterazioni corrente, la seconda significa letteralmente "ripeti ancora il ciclo".
Vediamo di seguito un semplice esempio:
CREATE PROCEDURE proc () BEGIN DECLARE var INT; SET var = 0; loop_label: LOOP INSERT INTO tbl VALUES (var); SET var = var + 1; IF var >= 10 THEN LEAVE loop_label; END IF; END LOOP; END; //
Anche il ciclo appena proposto presenta alcune similitudini con gli esempi analizzati in precedenza: il primo passaggio è ancora una volta quello relativo all'introduzione del nome della procedura seguito dalla creazione del blocco di istruzioni e dalla definizione di una variabile.
La parte relativa al ciclo viene introdotta grazie al costrutto loop_label: LOOP
a cui seguono le istruzioni da iterare fino alla fine del ciclo stesso; troviamo quindi un costrutto condizionale (IF...THEN
) che impone l'uscita dal ciclo (LEAVE loop_label
) nel momento in cui la variabile precedentemente dichiarata assume un valore maggiore o uguale a 10.
La parte finale della stored procedure è naturalmente dedicata alla chiusura dei diversi blocchi di istruzione annidati, per cui vengono chiusi rispettivamente il costrutto condizionale (END IF
), il ciclo stesso (END LOOP
) e la stored procedure (END; //
).
In pratica nel ciclo LOOP appena analizzato abbiamo trovato un costrutto IF
utilizzato come alternativa ad una condizione; all'interno del costrutto condizionale è stata invece inserita un'istruzione LEAVE
che in pratica significa "esci dal ciclo".
Etichettamento dei cicli
L'inserimento di una label
per un ciclo consente di isolare un gruppo di istruzioni ad esso riferito, con essa non facciamo altro che "etichettare" e quindi dare un nome al ciclo stesso. loop_label
, utilizzata per l'esempio precedente, è quindi un'etichetta che può essere sostituita con un'altra predefinita dall'utente, è però importante che per la chiusura di un blocco di istruzioni riguardante un ciclo venga utilizzata la stessa label
introdotta in apertura:
CREATE PROCEDURE proc () label_a: BEGIN label_b: WHILE 0 = 1 DO LEAVE label_b; END WHILE label_b; label_c: REPEAT LEAVE label_c; UNTIL 0 =0 END REPEAT label_c ; label_d: LOOP LEAVE label_d; END LOOP label_d ; END label_a ; //
Nell'esempio appena esposto l'itero blocco di istruzioni relativo alla procedura viene etichettato con label_a
, è logico quindi che la chiusura del blocco stesso faccia riferimento alla stessa etichetta; succede la medesima cosa per quanto riguarda ogni ciclo contenuto all'interno del blocco BEGIN .. END
, per cui all'etichettamento del ciclo WHILE come Label_b
corrisponderà un riferimento in chiusura a Label_b
e così via.
Vediamo ora un esempio di ciclo LOOP basato sull'introduzione della clausola ITERATE:
CREATE PROCEDURE proc () BEGIN DECLARE var INT; SET var = 0; loop_label: LOOP IF var = 5 THEN SET var = var + 1; ITERATE loop_label; END IF; INSERT INTO tbl VALUES (var); SET var = var + 1; IF var >= 10 THEN LEAVE loop_label; END IF; END LOOP; END; //
In questo caso, dopo l'etichettamento del loop abbiamo un blocco di istruzioni basato sul costrutto condizionale IF, al verificarsi della condizione non avviene un'uscita dal ciclo come nel caso di LEAVE ma una successiva iterazione imposta dalla clausola ITERATE; si chiude quindi il primo blocco IF ..END IF e si passa a delle nuove istruzioni da iterare, queste ultime verranno ripetute fino a quando verrà soddisfatta la condizione prevista dal secondo blocco IF ..END IF dopodiché potrà avvenire la definitiva uscita dal ciclo attraverso l'introduzione della clausola LEAVE.