Module:
SYSTEMES D’INFORMATION 2 ET BASE DE DONNEES 2
Matière:
SQL Server
Chapitre VI: Les curseurs
**********************
Professeure Nadia OUKRICH
[Link]@[Link]
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 1
Plan du cours
1. Présentation de l’environnement de SQLSERVER
2. Introduction au langage Transact SQL (T_SQL)
3. Structure d’un programme T_SQL
4. Les variables et leurs types
5. Les structures alternatives (de contrôle)
6. Les procédures stockées & Les fonctions stockées
7. La gestion des exceptions
8. Les curseurs
9. Les déclencheurs
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 2
Définition du curseur
Un curseur en T-SQL est un mécanisme qui permet de parcourir ligne par ligne
le résultat d'une requête, afin de traiter chaque enregistrement individuellement.
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 3
Avantages et inconvénients
Utilisation recommandée uniquement si :
✓ Il est nécessaire de traiter ligne par ligne,
✓ L’opération ne peut pas être facilement faite avec une requête SQL
classique,
✓ Tu dois appliquer des traitements complexes, conditionnels ou appeler des
procédures stockées pour chaque ligne.
Inconvénients
▪ Moins performant que les requêtes ensemblistes,
▪ Consomme plus de ressources (mémoire, CPU),
▪ À éviter sur de grandes tables.
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 4
Syntaxe générale
-- Déclaration du curseur
DECLARE nom_curseur CURSOR FOR
SELECT colonne1, colonne2 FROM ma_table;
-- Ouverture du curseur
OPEN nom_curseur;
-- Variables pour stocker les lignes
DECLARE @var1 INT, @var2 NVARCHAR(100);
-- Lecture initiale
FETCH NEXT FROM nom_curseur INTO @var1, @var2;
-- Boucle
WHILE @@FETCH_STATUS = 0
BEGIN
-- Traitement ligne par ligne
PRINT 'Valeur : ' + CAST(@var1 AS NVARCHAR) + ', ' + @var2;
-- Lire la ligne suivante
FETCH NEXT FROM nom_curseur INTO @var1, @var2;
END;
-- Fermeture et libération des ressources
CLOSE nom_curseur;
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 5
DEALLOCATE nom_curseur;
Syntaxe générale
1. Déclaration du curseur
DECLARE nom_curseur CURSOR FOR
SELECT colonne1, colonne2 FROM ma_table;
• Cette commande crée un curseur nommé nom_curseur basé sur le résultat de la
requête SELECT.
• Dans cet exemple, le curseur va parcourir chaque ligne du résultat de la requête SELECT
colonne1, colonne2 FROM ma_table.
2. Ouverture du curseur
OPEN nom_curseur;
• Cette commande exécute la requête SELECT liée au curseur et prépare le curseur à
lire les données ligne par ligne.
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 6
Syntaxe générale
3. Déclaration des variables
DECLARE @var1 INT, @var2 NVARCHAR(100);
• Ces variables vont recevoir les valeurs des colonnes pour chaque ligne parcourue par le
curseur :
➢ @var1 stocke colonne1 (de type entier)
➢ @var2 stocke colonne2 (chaîne de caractères).
4. Lecture initiale
FETCH NEXT FROM nom_curseur INTO @var1, @var2;
• Cette commande lit la première ligne du curseur et stocke les valeurs dans @var1 et
@var2.
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 7
Syntaxe générale
5. Boucle de traitement
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT 'Valeur : ' + CAST(@var1 AS NVARCHAR) +
', ' + @var2;
FETCH NEXT FROM nom_curseur INTO @var1, @var2;
END;
@@FETCH_STATUS retourne :
0 si la lecture a réussi (il y a une ligne),
-1 si c’est la fin du curseur,
-2 en cas d’erreur.
• Tant qu'il reste des lignes à lire (@@FETCH_STATUS = 0), la boucle continue :Il
affiche les valeurs (PRINT),Ensuite il lit la ligne suivante (FETCH NEXT...).
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 8
Syntaxe générale
6. Fermeture et libération du curseur
CLOSE nom_curseur;
DEALLOCATE nom_curseur;
CLOSE : Ferme le curseur (il n’est plus utilisable tant qu’il n’est pas rouvert).
DEALLOCATE : Supprime le curseur de la mémoire (libère les ressources).
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 9
Exemple 1: Curseur Simple
Afficher tous les noms de clients ainsi que les dates de leurs commandes:
DECLARE curseur_clients CURSOR FOR
SELECT [Link], [Link], CMD.Date_commande
FROM Client C
JOIN Commande CMD ON C.ID_Client = CMD.ID_Client;
DECLARE @Nom NVARCHAR(100), @Prenom NVARCHAR(100), @DateCommande DATE;
OPEN curseur_clients;
FETCH NEXT FROM curseur_clients INTO @Nom, @Prenom, @DateCommande;
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT 'Client : ' + @Nom + ' ' + @Prenom + ' - a Commandé le : ' +
CONVERT(NVARCHAR, @DateCommande, 103);
FETCH NEXT FROM curseur_clients INTO @Nom, @Prenom, @DateCommande;
END;
CLOSE curseur_clients;
DEALLOCATE curseur_clients;
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 10
Résultat de l’exemple1: Curseur Simple
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 11
Exemple 2: Curseur Simple
Mettre à jour les commissions des employés en fonction de leur ancienneté
DECLARE curseur_employes CURSOR FOR
SELECT ID_Employe, Date_entree, Commission
FROM Employe;
DECLARE @ID INT, @DateEntree DATE, @Commission FLOAT;
DECLARE @Anciennete INT;
OPEN curseur_employes;
FETCH NEXT FROM curseur_employes INTO @ID, @DateEntree, @Commission;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @Anciennete = DATEDIFF(YEAR, @DateEntree, GETDATE());
IF @Anciennete > 2
BEGIN
UPDATE Employe
SET Commission = Commission + 1000
WHERE ID_Employe = @ID;
PRINT 'Mise à jour : Employé ID ' + CAST(@ID AS NVARCHAR) +
'sa commission est: ' + CAST(@Commission AS NVARCHAR) + ' dh';
END
FETCH NEXT FROM curseur_employes INTO @ID, @DateEntree, @Commission;
END;
CLOSE curseur_employes;
DEALLOCATE curseur_employes;
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 12
Résultat Exemple 2: Curseur Simple
Mettre à jour les commissions des employés en fonction de leur ancienneté
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 13
Les options avancées du curseur
❑ Permet d’optimiser les performances et le comportement du curseur selon les
besoins.
DECLARE Nom_Curseur CURSOR
[ LOCAL | GLOBAL ]
[ FORWARD_ONLY | SCROLL ]
[ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ]
[ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ]
FOR
requête_SQL;
1- Portée du curseur (LOCAL / GLOBAL)
Option Explication Exemple
Le curseur est visible uniquement
Par défaut dans les procédures
LOCAL dans le bloc (BEGIN...END), procédure
stockées.
stockée, ou trigger où il est défini.
Le curseur est visible dans toute la Si tu veux accéder au curseur
GLOBAL
session SQL. après la fin du bloc de code.
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 14
Les options avancées du curseur
DECLARE Nom_Curseur CURSOR
[ LOCAL | GLOBAL ]
[ FORWARD_ONLY | SCROLL ]
[ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ]
[ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ]
FOR
requête_SQL;
2- Navigation dans le curseur (FORWARD_ONLY / SCROLL)
Option Explication Exemple
Lecture uniquement vers l'avant, Très rapide et faible en
FORWARD_ONLY
une ligne à la fois avec FETCH NEXT. ressources.
Permet de naviguer dans toutes les Plus de flexibilité, mais
SCROLL directions : FIRST, LAST, PRIOR, plus coûteux en en
ABSOLUTE, etc. ressources.
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 15
Les options avancées du curseur
DECLARE Nom_Curseur CURSOR
[ LOCAL | GLOBAL ]
[ FORWARD_ONLY | SCROLL ]
[ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ]
[ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ]
FOR
requête_SQL;
3- Type de curseur (comportement)
Option Explication Avantages / Inconvénients
Fait une copie complète des résultats dans le
+ Rapide pour lecture – Pas de
STATIC curseur. Ne reflète aucun changement dans la
mise à jour visible
table d'origine.
La structure (ensemble de lignes) est fixée à + Voit UPDATE – Ne voit pas
KEYSET
l'ouverture, mais les mises à jour sont visibles. INSERT/DELETE
Le curseur voit tous les changements sur la
DYNAMIC table (ajouts, suppressions, modifs) en temps + Réactif – Plus lent, gourmand
réel.
Combine FORWARD_ONLY + READ_ONLY. + Très performant – Pas de mise
FAST_FORWARD
Lecture rapide sans possibilité de modification. à jour
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 16
Les options avancées du curseur
DECLARE Nom_Curseur CURSOR
[ LOCAL | GLOBAL ]
[ FORWARD_ONLY | SCROLL ]
[ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ]
[ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ]
FOR
requête_SQL;
4- Concurrence / Verrouillage (READ_ONLY, SCROLL_LOCKS, OPTIMISTIC)
Option Explication Cas d’usage
Interdit toute tentative de Lecture uniquement.
READ_ONLY
modification via le curseur. Parfait pour reporting.
Verrouille chaque ligne lue, évite Lecture cohérente dans
SCROLL_LOCKS qu’elle soit modifiée par un autre les environnements
utilisateur. multi-utilisateurs.
Pas de verrouillage. Mais vérifie
Bon compromis entre
OPTIMISTIC que la ligne n’a pas changé avant
performance et intégrité.
l’UPDATE.
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 17
Exemple 1: Avec options avancées
Afficher tous les noms de clients ainsi que les dates de leurs commandes:
DECLARE curseur_clients CURSOR LOCAL FORWARD_ONLY FAST_FORWARD READ_ONLY
FOR
SELECT [Link], [Link], CMD.Date_commande
FROM Client C
JOIN Commande CMD ON C.ID_Client = CMD.ID_Client
WHERE CMD.Date_commande >= '2023-01-01'
ORDER BY CMD.Date_commande DESC;
DECLARE @Nom NVARCHAR(100),
@Prenom NVARCHAR(100),
@DateCommande DATE;
OPEN curseur_clients;
FETCH NEXT FROM curseur_clients INTO @Nom, @Prenom, @DateCommande;
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT 'Client : ' + @Nom + ' ' + @Prenom + ' - a commandé le : ' +
CONVERT(NVARCHAR, @DateCommande, 103);
FETCH NEXT FROM curseur_clients INTO @Nom, @Prenom, @DateCommande;
END;
CLOSE curseur_clients;
DEALLOCATE curseur_clients;
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 18
Exemple 1: Avec options avancées
L’intéret des options pour l’exemple :
DECLARE curseur_clients CURSOR LOCAL FORWARD_ONLY FAST_FORWARD READ_ONLY
FOR
….
Dans cet exemple, nous effectuons un parcours simple, en lecture seule, ligne par ligne,
sans retour en arrière, sans saut de ligne et sans modification des données.
Cela ne nécessite ni édition, ni navigation complexe, ce qui permet :
➢ de gagner en vitesse d’exécution ;
➢ de réduire l’utilisation de la mémoire et de tempdb ;
➢ de simplifier la gestion des ressources (moins de verrous, de journaux, etc.).
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 19
TP 7 – Partie 1
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 20
Exemple 2: avec Curseur statique
Mettre à jour les commissions des employés en fonction de leur ancienneté
DECLARE curseur_employes CURSOR STATIC FOR
SELECT ID_Employe, Date_entree, Commission
FROM Employe;
DECLARE @ID INT, @DateEntree DATE, @Commission FLOAT;
DECLARE @Anciennete INT;
-- Insertion OPEN curseur_employes;
dans la insert into Employe
table après VALUES (6, 'El Amrani', 'Khalid', '20210310', 'Manager', 'Marketing', 10000.00, 1200.00)
ouverture FETCH NEXT FROM curseur_employes INTO @ID, @DateEntree, @Commission;
WHILE @@FETCH_STATUS = 0
du curseur BEGIN
SET @Anciennete = DATEDIFF(YEAR, @DateEntree, GETDATE());
IF @Anciennete > 2
BEGIN
UPDATE Employe
SET Commission = Commission + 1000
WHERE ID_Employe = @ID;
PRINT 'Mise à jour : Employé ID ' + CAST(@ID AS NVARCHAR) +
'sa commission est: ' + CAST(@Commission AS NVARCHAR) + ' dh';
END
FETCH NEXT FROM curseur_employes INTO @ID, @DateEntree, @Commission;
END;
CLOSE curseur_employes;
DEALLOCATE curseur_employes;
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 21
Exemple 2: sans Curseur statique
Mettre à jour les commissions des employés en fonction de leur ancienneté
DECLARE curseur_employes CURSOR FOR
SELECT ID_Employe, Date_entree, Commission
FROM Employe;
DECLARE @ID INT, @DateEntree DATE, @Commission FLOAT;
DECLARE @Anciennete INT;
-- Insertion OPEN curseur_employes;
dans la insert into Employe
table après VALUES (7, ‘Rachid', ‘Salma', ‘20200101', ‘RH', ‘RH’, 20000.00, 2000.00)
ouverture FETCH NEXT FROM curseur_employes INTO @ID, @DateEntree, @Commission;
WHILE @@FETCH_STATUS = 0
du curseur BEGIN
SET @Anciennete = DATEDIFF(YEAR, @DateEntree, GETDATE());
IF @Anciennete > 2
BEGIN
UPDATE Employe
SET Commission = Commission + 1000
WHERE ID_Employe = @ID;
PRINT 'Mise à jour : Employé ID ' + CAST(@ID AS NVARCHAR) +
'sa commission est: ' + CAST(@Commission AS NVARCHAR) + ' dh';
END
FETCH NEXT FROM curseur_employes INTO @ID, @DateEntree, @Commission;
END;
CLOSE curseur_employes;
DEALLOCATE curseur_employes;
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 22
Exemple 2 avec Curseur statique: explication
Quand tu déclares un curseur avec le mot-clé STATIC :
• SQL Server prend un "instantané" (snapshot) des résultats de la requête
au moment où le curseur est ouvert (OPEN).
• Il stocke cette copie dans une table temporaire (dans tempdb), et ne
regarde plus la table réelle pendant le parcours du curseur.
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 23
CURSEUR SCROLL
Commande FETCH Description
FETCH NEXT FROM Lit la ligne suivante
FETCH PRIOR FROM Lit la ligne précédente
FETCH FIRST FROM Lit la première ligne
FETCH LAST FROM Lit la dernière ligne
FETCH ABSOLUTE n FROM Lit la nième ligne absolue (n =1: première, -1 : dernière)
FETCH RELATIVE n FROM Lit la ligne relative à la position actuelle (+1 = next, -1 = prior)
n négatif 1
n négatif
-1
ABSOLUTE n
RELATIVE n
+1
n positif
-1
n positif
SI n EST EN DEHORS DU CURSEUR: PAS DE DEPLACEMENT
14/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 24
Exemple 1: Avec Curseur SCROLL
Afficher tous les noms de clients ainsi que les dates de leurs commandes:
DECLARE curseur_clients SCROLL CURSOR FOR
SELECT [Link], [Link], CMD.Date_commande
FROM Client C
JOIN Commande CMD ON C.ID_Client = CMD.ID_Client;
DECLARE @Nom NVARCHAR(100), @Prenom NVARCHAR(100), @DateCommande DATE;
OPEN curseur_clients;
FETCH FIRST FROM curseur_clients INTO @Nom, @Prenom, @DateCommande;
PRINT 'Premier client : ' + @Nom + ' ' + @Prenom + ' - a Commandé le : ' +
CONVERT(NVARCHAR, @DateCommande, 103);
FETCH NEXT FROM curseur_clients INTO @Nom, @Prenom, @DateCommande;
PRINT 'Client suivant : ' + @Nom + ' ' + @Prenom + ' - a Commandé le : ' +
CONVERT(NVARCHAR, @DateCommande, 103);
FETCH PRIOR FROM curseur_clients INTO @Nom, @Prenom, @DateCommande;
PRINT 'Client précédent : ' + @Nom + ' ' + @Prenom + ' - a Commandé le : ' +
CONVERT(NVARCHAR, @DateCommande, 103);
FETCH LAST FROM curseur_clients INTO @Nom, @Prenom, @DateCommande;
PRINT 'Dernier client : ' + @Nom + ' ' + @Prenom + ' - a Commandé le : ' +
CONVERT(NVARCHAR, @DateCommande, 103);
FETCH ABSOLUTE 3 FROM curseur_clients INTO @Nom, @Prenom, @DateCommande;
PRINT 'Client en position 3 : ' + @Nom + ' ' + @Prenom + ' - a Commandé le : ' +
CONVERT(NVARCHAR, @DateCommande, 103);
FETCH RELATIVE -2 FROM curseur_clients INTO @Nom, @Prenom, @DateCommande;
PRINT 'Client 2 positions avant : ' + @Nom + ' ' + @Prenom + ' - a Commandé le : ' +
CONVERT(NVARCHAR, @DateCommande, 103);
CLOSE curseur_clients;
DEALLOCATE curseur_clients;
Exemple 1: Avec Curseur SCROLL
Afficher tous les noms de clients ainsi que les dates de leurs commandes:
Extrait de La table Client
…..
…..
Résultat de l’Exemple 1: Avec Curseur SCROLL
FETCH FIRST
FETCH NEXT
FETCH PRIOR
FETCH LAST
FETCH ABSOLUTE 3
FETCH RELATIVE -2
14/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 26
Curseur KEYSET
Fonction Description
Permet de naviguer dans toutes les directions (FIRST, LAST, NEXT,
Lecture
PRIOR, ABSOLUTE, RELATIVE).
Les modifications (UPDATE) effectuées sur les lignes de la table sont
Mise à jour
visibles via le curseur.
Les lignes supprimées sont visibles comme “non disponibles” et ne
Suppression
peuvent plus être lues par le curseur (@@FETCH_STATUS = -2)
Les nouvelles lignes insérées après l’ouverture du curseur ne sont
Insertion
pas visibles.
13/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 27
Exemple 3: Curseur KEYSET
Utilisation du curseur KEYSET pour l'affichage des clients:
DECLARE @ID INT, @NOM VARCHAR(50), @PRENOM VARCHAR(50), @ADRESSE VARCHAR(50),
@VILLE VARCHAR(50), @PAYS VARCHAR(50);
DECLARE usdCURSOR_KEYSET CURSOR
KEYSET FOR SELECT ID_Client, NOM, PRENOM, ADRESSE, VILLE, PAYS FROM CLIENT;
OPEN usdCURSOR_KEYSET;
IF @@CURSOR_ROWS> 0
BEGIN FETCH NEXT FROM usdCURSOR_KEYSET INTO @ID, @NOM, @PRENOM, @ADRESSE,
@VILLE, @PAYS
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT 'ID= ' + CONVERT (VARCHAR (10), @ID) + ', Nom complet= ' +
@NOM+''+@PRENOM+ ', ADRESSE= ' + @ADRESSE + ', VILLE= ' + @VILLE + ',
PAYS= ' + @PAYS;
FETCH NEXT FROM usdCURSOR_KEYSET
INTO @ID, @NOM, @PRENOM, @ADRESSE, @VILLE, @PAYS
END
END
CLOSE usdCURSOR_KEYSET
DEALLOCATE usdCURSOR_KEYSET
14/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 28
Exemple 3: Curseur KEYSET
Exemple d'utilisation du curseur KEYSET avec test de modification et insertion:
DECLARE @ID INT, @DateEntree DATE, @Commission DECIMAL(10,2);
DECLARE employeCURSOR_KEYSET CURSOR
KEYSET FOR
SELECT ID_Employe, Date_entree, Commission
FROM Employe;
OPEN employeCURSOR_KEYSET;
Modification -- Modification (visible par le curseur KEYSET)
UPDATE Employe
SET Commission = ISNULL(Commission, 0) + 500
WHERE ID_Employe = 1;
Insertion -- Insertion (non visible car le curseur est déjà ouvert)
INSERT INTO Employe (ID_Employe, Nom, Prenom, Date_entree, Titre, Servicee, Salaire, Commission)
VALUES (9, 'Sabri', 'Noura', '2021-01-15', 'Assistante', 'RH', 7000.00, 800.00);
IF @@CURSOR_ROWS > 0
BEGIN
FETCH NEXT FROM employeCURSOR_KEYSET INTO @ID, @DateEntree, @Commission;
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT 'ID = ' + CAST(@ID AS VARCHAR) +
', Date d''entrée = ' + CONVERT(VARCHAR, @DateEntree) +
', Commission = ' + CAST(@Commission AS VARCHAR);
FETCH NEXT FROM employeCURSOR_KEYSET INTO @ID, @DateEntree, @Commission;
END
END
CLOSE employeCURSOR_KEYSET;
DEALLOCATE employeCURSOR_KEYSET;
Exemple 3: Curseur KEYSET
Résultat du test de modification et insertion : Exemple d'utilisation du curseur KEYSET :
Le curseur KEYSET :
•ne voit pas les lignes insérées après son ouverture : 9, 'Sabri', 'Noura'…
• voit les modifications de lignes existantes: La commission ligne 1
14/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 30
TP 7 – Partie 2
14/05/2025 Pr. Nadia Oukrich. SQL Server 3IIR 31