
Le curseur est un mécanisme de mise en mémoire en tampon permettant de parcourir les
lignes d'enregistrements du résultat renvoyé par une requête. Les curseurs sont envoyés par
MS-SQL Server tout le temps, mais on ne voit pas le mécanisme se passer, ainsi lors d'une
requête SELECT, SQL Server va employer des curseurs.
Pour utiliser un curseur, il faut commencer par le déclarer :
DECLARE name CURSOR FOR
SELECT …
On peut aussi l'utiliser avec de la modification en ajoutant FOR UPDATE à la fin de la
requête, bien que ce ne soit pas conseillé.
Ensuite, il faut ouvrir ce curseur avec OPEN name et ne pas oublier de le fermer à la fin avec
CLOSE name. Il faut aussi utiliser DEALLOCATE pour libérer la mémoire du curseur.
Pour récupérer les valeurs actuelles contenues dans le curseur, il faut employer :
FETCH name INTO @value1, @value2 …
Cela va stocker les valeurs actuelles de l'enregistrement courant dans les variables @valueX,
qu'il ne faut surtout pas oublier de déclarer.
On peut néanmoins utiliser FETCH pour d'autres choses :
176
• Aller à la première ligne : FETCH FIRST FROM curseur_nom
• Aller à la dernière ligne : FETCH LAST FROM curseur_nom
• Aller à la ligne suivante : FETCH NEXT FROM curseur_nom
• Aller à la ligne précédente : FETCH PRIOR FROM curseur_nom
• Aller à la ligne X : FETCH ABSOLUTE ligne FROM curseur_nom
• Aller à X lignes plus loin que l'actuelle : FETCH RELATIVE ligne FROM curseur_nom
Pour parcourir un curseur, on peut employer une boucle WHILE qui teste la valeur de la
fonction @@FETCH_STATUS qui renvoie 0 tant que l'on n'est pas à la fin.
DECLARE @numfou VARCHAR(50)
DECLARE curseur_comparfou CURSOR FOR
SELECT OBSCOM FROM vente.ENTCOM
OPEN curseur_comparfou
FETCH curseur_comparfou INTO @numfou
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT @numfou
FETCH curseur_comparfou INTO @numfou
END
CLOSE curseur_comparfou
DEALLOCATE curseur_comparfou
Ici, nous récupérons toutes les observations de la colonne OBSCOM de la table ENTCOM.
Modifions l’exemple précèdent de façon à l’incorporé dans une procédure stockée et de nous
donner les observations d’un fournisseur précis.
CREATE PROCEDURE recherche_ligne_com
-- On déclare nos variables
@numfou VARCHAR(50)
AS
BEGIN
-- On désigne curseur_comparfou
DECLARE curseur_comparfou CURSOR FOR
-- Le curseur agi pour la sélection suivante
SELECT OBSCOM FROM vente.ENTCOM WHERE NUMFOU = @numfou
-- On ouvre le curseur
OPEN curseur_comparfou
FETCH curseur_comparfou INTO @numfou
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT @numfou
FETCH curseur_comparfou INTO @numfou
177
END
-- On ferme le curseur
CLOSE curseur_comparfou
-- On le vide de la mémoire
DEALLOCATE curseur_comparfou
END
GO
On interroge notre procédure stockée :
EXEC recherche_ligne_com3 '120'
On peut bien entendu imbriquer plusieurs curseurs les uns dans les autres pour des choses
plus compliquées.
Concrètement, maintenant que nous avons vu comment fonctionnait un curseur et comment
l'employer, que fait-il de plus qu'une simple requête ? Il permet surtout d'intervenir sur le
résultat de la requête. On peut intervenir sur chaque valeur retournée, on peut modifier ces
valeurs ou supprimer des lignes. On peut aussi réaliser des opérations avec ces données
avant qu'elles arrivent au programme qui les utilise, c'est à dire des calculs de somme, des
maximums, des modifications de date, des formatages de chaînes de caractères.
Un exemple intéressant est le parcours avec rupture, c'est à dire parcourir et si on a déjà eu
une fois cet objet on ne le réaffiche pas. Dans l'exemple que je vais vous présenter, on affiche
tous les genres, les acteurs par genre et pour chaque acteur les livres qu'ils ont écrits. On
emploie des ruptures pour vérifier que l'on n'a pas déjà affiché une fois cet élément :
DECLARE @titre VARCHAR(50), @genre VARCHAR(50), @rupture_genre VARCHAR(50),
@rupture_auteur VARCHAR(50), @auteur VARCHAR(50)
DECLARE @i_genre INT
SET @i_genre = 1
SET @rupture_genre = ''
SET @rupture_auteur = ''
DECLARE curseur_ouvrages CURSOR FOR
SELECT ouvrage_titre, genre_nom, auteur_nom FROM t_ouvrages O
LEFT OUTER JOIN t_genres
ON genre_id = ouvrage_genre
LEFT OUTER JOIN t_ouvrages_auteurs TOA
ON TOA.ouvrage_id = O.ouvrage_id
LEFT OUTER JOIN t_auteurs TA
ON TA.auteur_id = TOA.auteur_id
ORDER BY ouvrage_genre
OPEN curseur_ouvrages
FETCH curseur_ouvrages INTO @titre , @genre, @auteur
WHILE @@FETCH_STATUS = 0
BEGIN
178
IF @genre != @rupture_genre
BEGIN
PRINT ''
PRINT CONVERT(CHAR(2),@i_genre) + '. ' + @genre
SET @i_genre = @i_genre + 1
SET @rupture_auteur = ''
END
IF @auteur != @rupture_auteur
BEGIN
PRINT ''
PRINT @auteur
PRINT '------------'
END
PRINT @titre;
SET @rupture_genre = @genre
SET @rupture_auteur = @auteur
FETCH curseur_ouvrages INTO @titre , @genre, @auteur
END
CLOSE curseur_ouvrages
DEALLOCATE curseur_ouvrages